Adding and Loading objects from ASP.NET Session via Attributes - c#

I'm having a controller class like
class MyController : Controller
{
User user
{
get
{
if(Session["User"] == null)
Session["User"] = // Constructing object here
return Session["User"];
}
}
....
}
I have lot of properties like User in this controller. I want to get/set without all this
if(Session["User"] == null)
noises. It could be something with attribute like
class MyController : Controller
{
[SessionCacheObject("User")]
User user;
}
How do I do this or is there any other smart way?

Look at my User session.
public static aspnet_Users LogInUser
{
get
{
if (HttpContext.Current.Session["LogInUser"] != null)
{
return (aspnet_Users)HttpContext.Current.Session["LogInUser"];
}
else
{
return null;
}
}
set
{
HttpContext.Current.Session["LogInUser"] = value;
}
}
Now, whenever add data to session like below.
LogInUser = user; // get data from wherever you need and set it.

Related

Create/Use property to get Session variable C#

I have an ASPX Web Forms control that imports from another project the Person class.
public partial class cuTest : System.Web.UI.UserControl
There I have created in that control a property for an object session and a method to populate that session.
public Person person
{
get
{
if (this.Session["Person"] == null)
{
Session["Person"] = new Person();
}
return Session["Person"] as Person;
}
}
private void crearPago()
{
this.person.name = "Max";
this.person.surname = "Ford";
}
Now when I want to call it from the page that contains that control.
var x = cuTest.person; I need to check if it's not empty since this can't be null. How can I do that?
I have a static class Web that I add there any properties that are useful throughout the application.
One thing that differs is that the Request, Response and Session objects are not directly available. So, I have 3 helper functions that return the current Request, Response and Session objects, if available.
So, a Person person property would be like that:
using System;
using System.Web;
using System.Web.SessionState;
namespace MyWebApp
{
public static class Web
{
private static HttpRequest request
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Request;
}
}
private static HttpResponse response
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Response;
}
}
private static HttpSessionState session
{
get
{
if (HttpContext.Current == null) return null;
return HttpContext.Current.Session;
}
}
public static Person person
{
get
{
// Here you can change what is returned when session is not available
if (session == null) return null;
if(session["Person"] == null) {
session["Person"] = new Person();
}
return session["Person"] as Person;
}
set
{
// Here you can change how to handle the case when session is not available
if (session == null) return;
session["Person"] = value;
}
}
}
}
To use it, in your Page or UserControl code, you can write
// get
var x = MyWebApp.Web.person;
// set
MyWebApp.Web.person = x;
You can simply check if the name/surname is null or empty with:
string.IsNullOrEmpty(x.name);

Using Session out of the Controller class

I am thinking to implement something like a static SessionHelper class where I would like to keep some data in Session.
But it seems like is impossible to use Session object out of the Controller class. Right?
Or may be I am wrong... I.e. is this link a solution ASP.NET MVC - How to access Session data in places other than Controller and Views
Let me know, please!
Anyway for now I cannot refer to Session object in that class which lives in Models folder.
public static class SessionHelper
{
public static bool ShowSuccessPopup
{
get
{
if (Session["ShowSuccessPopup"] == null)
{
Session["ShowSuccessPopup"] = false;
return false;
}
else
{
var result = (bool)Session["ShowSuccessPopup"].ToString();
return result;
}
}
set {Session["ShowSuccessPopup"] = value; }
}
}
The Session object is only set in the request-cycle, so anything outside the request-cycle won't have access to it (i.e. Controllers and Views are fine, but models no). If you need to work with the session in something outside of the request-cycle, then you must inject the Session object as a dependency. However, you're not going to be able to accomplish that with a static class. So you might instead try something like:
public class SessionHelper
{
private HttpSessionState session;
public SessionHelper (HttpSessionState session)
{
this.session = session;
}
public bool ShowSuccessPopup { ... }
}
Alternatively, you may be able to get by with merely injecting the session into your actual methods individually, but you wouldn't be able to continue using a property:
public static bool ShowSuccessPopup (HttpSessionState session)
{
// do something with session
}
Thanks to Chris Pratt!
Just like an alternative I am gonna share my approach.
public partial class BaseController : Controller
{
public SessionBox SessionBox;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
SessionBox = new SessionBox(filterContext.HttpContext);
base.OnActionExecuting(filterContext);
}
}
public class SessionBox
{
private HttpContextBase context { get; set; }
public SessionBox(HttpContextBase context)
{
this.context = context;
}
public bool ShowSuccessPopup
{
get
{
if (context.Session["ShowSuccessPopup"] == null)
{
context.Session["ShowSuccessPopup"] = false;
return false;
}
else
{
var result = Convert.ToBoolean(context.Session["ShowSuccessPopup"].ToString());
return result;
}
}
set { context.Session["ShowSuccessPopup"] = value; }
}
}
Notice that you should inheritine Controller class on BaseController class
and later in the Controller class you can do like
if (SessionBox.ShowSuccessPopup)
{
SessionBox.ShowSuccessPopup = false;
Here are extra links that demonstrate difference between
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.aspx
and
http://msdn.microsoft.com/en-us/library/system.web.httpcontextbase.aspx

Reflection set value of a properties property

I have 2 classes:
public class CustomerViewModel {
public SystemViewModel system { get;set; }
}
public class SystemViewModel {
public bool isReadOnly { get; set; }
}
On the method controller action I have a custom filter attribute which executes some code and determines whether or the user has ReadOnly or Write access. This attribute can be applied to multiple actions across multiple controllers.
So far using reflection I can get access to the model using:
var viewModel = filterContext.Controller.ViewData.Model;
I can not cast this model to CustomerViewModel because on a different action it might be something like SalaryViewModel. What I do know is that any model that requires the readonly property will have SystemViewModel property.
From my custom filter I need a way to be able to change the value of readonly.
So far I have this:
public override void OnActionExecuted(ActionExecutedContext filterContext) {
var viewModel = filterContext.Controller.ViewData.Model;
var systemViewModelPropertyInfo = model.GetType()
.GetProperties()
.FirstOrDefault(p => p.PropertyType == typeof(SystemViewModel));
if (systemViewModelPropertyInfo != null) {
// Up to here, everything works, systemViewModelPropertyInfo is of
// type PropertyInfo, and the systemViewModelPropertyInfo.PropertyType
// shows the SystemViewModel type
// If we get here, the model has the system property
// Here I need to try and set the IsReadOnly property to true/false;
// This is where I need help please
}
}
SOLVED
Thanks to everyone who pitched in to help solve this. Special thanks to Julián Urbano for having the solution I was looking for.
Here is my resulting code from within my filter:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
try
{
var viewModel = filterContext.Controller.ViewData.Model;
var systemViewModelPropertyInfoCount = viewModel.GetType().GetProperties().Count(p => p.PropertyType == typeof(SystemViewModel));
if(systemViewModelPropertyInfoCount == 1)
{
var systemViewModelPropertyInfo = viewModel.GetType().GetProperties().First(p => p.PropertyType == typeof(SystemViewModel));
if(systemViewModelPropertyInfo != null)
{
var systemViewModel = systemViewModelPropertyInfo.GetValue(viewModel, null) as SystemViewModel;
if(systemViewModel != null)
{
var admin = GetAdmin(filterContext.HttpContext.User.Identity.Name);
if(admin != null && _adminService.HasPermission(admin, _privilege, Access.Level.ReadWrite))
systemViewModel.ReadOnly = false;
else
systemViewModel.ReadOnly = true;
}
}
} else if(systemViewModelPropertyInfoCount > 1)
{
throw new Exception("Only once instance of type SystemViewModel allowed");
}
}
catch (Exception exception)
{
Log.Error(MethodBase.GetCurrentMethod(), exception);
filterContext.Controller.TempData["ErrorMessage"] = string.Format("Technical error occurred");
filterContext.Result = new RedirectResult("/Error/Index");
}
finally
{
base.OnActionExecuted(filterContext);
}
}
I can not cast this model to CustomerViewModel because on a different action it might be something like SalaryViewModel. What I do know is that any model that requires the readonly property will have SystemViewModel property.
option 1
Seems to me that the best option is to write an interface like:
public interface IWithSystemViewModel {
SystemViewModel System {get;}
}
and implement it from your classes, much like:
public class CustomerViewModel : IWithSystemViewModel{
public SystemViewModel System { get;set; }
}
public class SalaryViewModel : IWithSystemViewModel{
public SystemViewModel System { get;set; }
}
so you can cast it and access the isReadOnly property:
IWithSystemViewModel viewModel = filterContext.Controller.ViewData.Model as IWithSystemViewModel;
if(viewModel!=null){
viewModel.System.isReadOnly ...
}
option 2
If you want to stick to using reflection:
var viewModel = filterContext.Controller.ViewData.Model;
SystemViewModel theSystem = viewModel.GetType().GetProperty("system")
.GetValue(viewModel, null) as SystemViewModel;
theSystem.isReadOnly ...
Tricky thing: in your code, you select the property whose type is SystemViewModel. But what if the object actually has several SystemViewModel properties that you don't know about? Are you sure you're accessing the proper one? You may force all of them to use the same name, but then again, that would be like using the interface in option 1 above.
I'd definitely go with option 1.
var viewModel = new CustomerViewModel();
var systemViewModelPropertyInfo = viewModel.GetType()
.GetProperties()
.FirstOrDefault(p => p.PropertyType == typeof(SystemViewModel));
if (systemViewModelPropertyInfo != null) {
var systemViewModelProperty = systemViewModelPropertyInfo.GetValue(viewModel, null) as SystemViewModel;
// get the desired value of isReadOnly here...
var isReadOnly = false;
// here, systemViewModelProperty may be null if it has not been set.
// You can decide what to do in that case. If you need a value to be
// present, you'll have to do something like this...
if (systemViewModelProperty == null) {
systemViewModelPropertyInfo.SetValue(viewModel, new SystemViewModel { isReadOnly = isReadOnly }, null);
}
else {
systemViewModelProperty.isReadOnly = isReadOnly;
}
}
That said, this whole process would probably be easier if you implemented an interface...
public interface IHaveSystemViewModel {
SystemViewModel system { get; set; }
}
var model = viewModel as IHaveSystemViewModel;
if (model != null) {
// again, you need to make sure you actually have a reference here...
var system = model.system ?? (model.system = new SystemViewModel());
system.isReadOnly = false; // or true
}

How should I report a duplicated business entity/object on service layer?

I am trying to code with best practices and I have a doubt here. I am testing this on WebForms.
I have a UserService Layer where I have a method to pass a user to the RepositoryLayer:
public AddUserResponse AddUserResponse(AddUserRequest addUserRequest)
{
AddUserResponse response = new AddUserResponse();
User objUser = new User();
objUser.Names = addUserRequest.Names;
objUser.LastName = addUserRequest.LastName;
objUser.Email = addUserRequest.Email;
objUser.Alias = addUserRequest.Alias;
objUser.Profile.IdProfile = addUserRequest.Profile.IdProfile;
objUser.Password = addUserRequest.Password;
objUser.Active = addUserRequest.Active;
short OperationState=_userRepository.Add(objUser);
if (OperationState==0)
{
response.State=true;
response.Message="User inserted";
}
else if (OperationState==2)
{
response.State=false;
response.Message="Alias or Email already exist. Cannot insert User";
}
else
{
response.State=false;
response.Message="Error in User insertion";
}
return response;
}
Then I have a UserRepository Layer where I have a function that Adds a user comming from my service layer:
public short Add(User objUser)
{ ... return OperationState }
As showed this function relays on a stored procedure call to insert the user record. If the user email or alias doesn't exist then it inserts and returns 0, if it does returns 2, and if the operation fails returns 1.
I perform the checking and insertion in one call to save database round trips.
Am I performing the checking in a correct way on my service and repository classes?, or if am not, How should I abstract the logic to let the system determine when is a duplicated user?. Should I use the model or service to put the validation logic and raise a custom exception when that happens?
Thanks a lot for your insight.
UPDATE
For general interest I am posting now how I am implementing this on my App, once I get the Jason's IoC solution gonna make an update on this as well.
Model Class:
using ABC.DEF.Infrastructure.Domain;
namespace ABC.DEF.Model
{
public class AliasOrEmailAreUnique
{
private readonly IRepository<User, int> repository;
public AliasOrEmailAreUnique(IRepository<User,int> repository)
{
this.repository = repository;
}
//If the user is added has Id 0 so search among all the existing users for one that could have the alias or email registered already
//else if the user is being edit then search among all the user except the user with such Id(itself)
public bool IsBroken(User model)
{
if (model.IdUser == 0)
{
return (
repository.List().Where(x => x.Alias == model.Alias).Any()
|| repository.List().Where(x => x.Email == model.Email).Any()
);
}
else
{
return (
repository.List().Where(x => x.Alias == model.Alias && x.IdUser != model.IdUser).Any()
|| repository.List().Where(x => x.Email == model.Email && x.IdUser != model.IdUser).Any()
);
}
}
public ErrorMessage ErrorMessage
{
get { return new ErrorMessage { Property = "AliasEmail", Message = "Alias or Email exists already" }; }
}
}
}
Service Class:
using ABC.DEF.Repository;
using ABC.DEF.Model;
using ABC.DEF.Service.Messaging.User;
namespace ABC.DEF.Service
{
public class UsuarioService
{
public AddUserResponse AddUserResponse(AddUserRequest addUserRequest)
{
AddUserResponse response = new AddUserResponse();
User objUser = new User();
objUser.Names = addUserRequest.Names;
objUser.LastName = addUserRequest.LastName;
objUser.Email = addUserRequest.Email;
objUser.Alias = addUserRequest.Alias;
objUser.Profile.IdProfile = addUserRequest.Profile.IdProfile;
objUser.Password = addUserRequest.Password;
objUser.Active = addUserRequest.Active;
//Determine if the Alias or Email are unique
Model.AliasOrEmailAreUnique aliasOrEmailAreUnique = new Model.AliasOrEmailAreUnique(_userRepository);
if (!aliasOrEmailAreUnique.IsBroken(objUser))
{
_usuarioRepository.Add(objUser);
response.State = true;
response.Message = "User added succesfully";
}
else
{
response.State = false;
response.Message = aliasOrEmailAreUnique.ErrorMessage.Message;
}
return response;
}
}
}
I like to validate the input at the beginning of a unit of work. For a web application the request is the unit of work. before the controller action is fired I validate the user input. the action itself is the "happy path". if it makes it this far I know my operation will succeed. at the end the request (the response) I commit any changes back to the database.
I also like to keep my operation explicit so a call to add an entity would be different than call to edit an entity vs. deleting an entity.
in your scenario you have a service layer rather than controller actions, but the process is still the same. validate the model before calling the service layer. then pass the model to the service layer to preform what operations you want.
...UPDATE 1...
in response to your comment below.....
I have been calling my repositories only in the service layer
Becareful not to fall into the trap of thinking there is a linear patterns for making calls. through the application. instead think of it as an onion or sphere with multiple layers.
The model is just a POCO/DTO. there would be other components responsible for validating the model. typically I have a business rules engine that looks something like this... written off the top of my head.
interface IRule<T>
{
bool IsBroken(T model);
ErrorMessage Message {get;}
}
interface IRulesEngine
{
IEnumerable<ErrorMessage> Validate<T>(T model);
}
class ErrorMessage
{
public string Property {get;set;}
public string Message {get;set;}
}
class RulesEngine : IRulesEngine
{
private readonly IContainer container;
public RulesEngine(IContainer container)
{
this.container = container;
}
public IEnumerable<ErrorMessage> Validate<T>(T model)
{
return container
.GetInstances<IRule<T>>()
.Where(rule => rule.IsBroken(model))
.Select(rule => rule.Message);
}
}
this implementation assumes an IoC container, but can be implemented without one. The a rule may look like this
class NameIsUnique<MyClass> : IRule<MyClass>
{
private readonly IRepository<TheEntity> repository;
public NameIsUnique<MyClass>(IRepository<TheEntity> repository)
{
this.repository = repository;
}
public bool IsBroken(MyClass model)
{
return repository.Where(x => x.Name == model.Name).Any();
}
public ErrorMessage
{
get { return new ErrorMessage { Property = "Name", Message = "Name is not unique" }; }
}
}
finally, how this can be used.
var errors = engine.Validate(model);
LoadErrorsInToView(errors);
if(errors.Any()) return;
//call service to process the happy path...
...UPDATE 2...
first we refactor our interfaces
//this is just a marker interface. don't directly imeplement this.
interface IRule
{
}
interface IRule<T> : IRule
{
bool IsBroken(T model);
ErrorMessage Message {get;}
}
class RulesEngine : IRulesEngine
{
public reasdonly ICollection<IRule> Rules = new List<IRule>();
public IEnumerable<ErrorMessage> Validate<T>(T model)
{
return Rules
.Where(x => typeof(IRule<T>).IsAssignableFrom(x.GetType()))
.Cast<IRule<T>>()
.Where(rule => rule.IsBroken(model))
.Select(rule => rule.Message);
}
}

How do I get the C# IErrorDataInfo to work with ASP.MVC 3

I've verified my IErrorDataInfo implementation on my Model works but I can't seem to get the validations error from IErrorDataInfo on my model to the ASP.MVC ModelState in my controller. What is the best way to do this?
I could loop through my results and manually add them to the ModelState but there has to be an easier way.
Here is my controller from the client.
[HttpPost]
public ActionResult Edit(Grade grade)
{
MvcApplication.Container.BuildUp(grade);
if (!ModelState.IsValid)
{
return View("Edit", grade);
}
try
{
var provider = MvcApplication.Container.Resolve<IGradeProvider>();
provider.Edit(grade);
return RedirectToAction("Details", "Grade", new { id = grade.GradeId });
}
catch
{
return RedirectToAction("Index");
}
}
Here is my IErrorDataInfo implimentation
public abstract class AbstractBase : IAbstractBase
{
#region IDataErrorInfo
public abstract ValidationResult SelfValidate();
public bool IsValid
{
get
{
return SelfValidate().IsValid;
}
}
public string Error
{
get
{
var results = SelfValidate().Errors.Select(s => string.Format("● {0}{1}", s.ErrorMessage, Environment.NewLine)).ToArray();
return string.Join("", results);
}
}
public string this[string columnName]
{
get
{
var validationResults = SelfValidate();
if (validationResults == null) return string.Empty;
var columnResults = validationResults.Errors.FirstOrDefault(x => string.Compare(x.PropertyName, columnName, true) == 0);
return columnResults != null ? columnResults.ErrorMessage : string.Empty;
}
}
#endregion
}
UPDATE:
I should have added that I'm using dependency injection. The problem was not that the IErrorDataInfo was not working with ASP.MVC 3, the problem was that IErrorDataInfo need an injected parameter to work correctly. To fix the issue I manually called TryValidateModel after the buildup of the object but before the ModelState.IsValid is called.
The DataErrorInfoModelValidatorProvider is using Item member of IDataErrorInfo for gathering property level validation errors and Error property for Model level validation error:
public string Error
{
//This is needed for model level validation to work
get { throw new NotImplementedException(); }
}
public string this[string propertyName]
{
//This is needed for property level validation to work
get { throw new NotImplementedException(); }
}
This all should work out of the box (if you haven't disabled the DataErrorInfoModelValidatorProvider) when properly implemented.
UPDATE:
If your model isn't ready for validation during the model binding process (or you don't want it to be binded by ASP.NET MVC), you need to manually validate the model before you call the ModelState.IsValid method. Your controller might look like this:
[HttpPost]
public ActionResult Edit(Grade grade)
{
MvcApplication.Container.BuildUp(grade);
//Add this to your controller.
TryValidateModel(grade);
if (!ModelState.IsValid)
{
return View("Edit", grade);
}
try
{
var provider = MvcApplication.Container.Resolve<IGradeProvider>();
provider.Edit(grade);
return RedirectToAction("Details", "Grade", new { id = grade.GradeId });
}
catch
{
return RedirectToAction("Index");
}
}

Categories

Resources