Where to put validation for changing an object's children? - asp.net-mvc

I am using ASP.NET MVC 2 with nhibernate. I have this Sales class. Sales has many Payments. Sales' payments should not be modified once the sales' status becomes confirmed. I need suggestions on how to enforce this.
I'm stumped on several things when trying to put this validations:
For adding and deleting payments:
I can create AddPayment and DeletePayment methods in Sales, but everybody must remember to use these instead of adding and deleting the payments collection directly
I don't want to hide the payments collection because nhibernate needs it, also this collection is used in other parts of the software
For modifying existing payments:
I don't think I should put the validation in the Payments setters because nhibernate needs to access the setters.
Should I throw exception? There's a discussion about the disadvantages of throwing exception to prevent object entering invalid state. In my case object state after modification may still be valid, but I want to block the modification. Is throwing exception reasonable in this case? What are the alternatives?
Should I enforce this in the controller actions?

You can hide the collection by making it protected or even private. NHibernate will still find it. Alternatively you could have the collection getter return an immutable collection when the sales status is the restricted value.
private ISet<Payment> _payments;
public virtual ISet<Payment> Payments
{
get
{
if (Status == SalesStatus.Confirmed)
return new ImmutableSet<Payment>(_payments);
return _payments;
}
private set { _payments = value; }
}
Putting validation rules in the setters is also fine. You can tell NHibernate to access the backing field directly (you may have to add a backing field if you're currently using auto properties).
<property name="_name" access="field"/>
<property name="Description" access="nosetter.camelcase-underscore"/>
Edit for the additional question...
I don't tend to throw exceptions until right before a save. Throwing them earlier, like in a property setter, can be annoying to UI developers who have to go back to the user with just one error at a time. I do mostly MVC applications too, so I've been using the System.ComponentModel.DataAnnotations validation attributes of late. While limited in some respects, they work well with MVC for the model checks at the browser and in the controller. If you use those, however, I recommend creating a custom interceptor to check them all right before a save. The interceptor is where I will throw an exception if anything is awry.

There are several options:
One is to have a trigger in the database, then you would be 100% sure
Your suggestion of AddPayment and DeletePayment, combined with some code review is probably the best way to go.

I would introduce two new projects (layers) in your solution:
a model layer
a service (or business) layer
Your model layer should consist of interfaces and supporting types:
public interface ISales
{
IEnumerable<IPayment> GetPayments();
}
public enum PaymentStatus
{
Unknown,
Confirmed
}
public interface IPayment
{
// your public properties
PaymentStatus Status { get; set; }
}
You should move your NHibernate classes into the service layer and hide them with internal. Your NHibernate classes implement the model interfaces:
internal class Sales : ISales
{
public IEnumerable<IPayment> GetPayments()
{
// your implementation
}
}
internal class Payment : IPayment
{
// your public properties
public PaymentStatus Status { get; set; }
}
public class SalesService
{
public ISales FindByKey(int key)
{
// your implementation
}
public void AddPayment(ISales sales, IPayment payment)
{
// throw exception if validation fails
}
public void DeletePayment(ISales sales, IPayment payment)
{
// throw exception if validation fails
}
}
public class PaymentService
{
public IPayment FindByKey(int key)
{
// your implementation
}
}
Since the classes Sales and Payment are hidden, your controllers are foced to use the service classes:
public class SalesController : Controller
{
private readonly SalesService salesService;
private readonly PaymentService paymentService;
public SalesController()
{
salesService = new SalesService();
paymentService = new PaymentService();
}
public ActionResult AddPayment(int salesId, int paymentId)
{
var sales = salesService.FindByKey(salesId);
var payment = paymentService.FindByKey(paymentId);
salesService.AddPayment(sales, payment);
return RedirectToAction("Index");
}
}
You should also consider using an IoC container like autofac or Ninject.

Related

How to use a Session provider in a DI project

I am developing a web application in ASP.NET MVC5.
Like all basic web applications it also has a login page where a user can authenticate himself. Once authenticated I want to store a couple of user-related items in the Session so I don't have to query the database every time to reconstruct the authenticated user.
After having read Mark Seemann's book about Dependency Injection I want to loosely couple all my layers and make sure that everything can easily be replaced.
At the moment my SessionProvider is by default using the Session object, but maybe in the future this could change to another type of storage mechanism.
The approach I have taken is by using Ambient Context which he explained with the TimeProvider example, but I am wondering if this is the right approach for this functionality and if it is thread safe (also for unit testing).
Is my solution proper or how would you implement such a mechanism? This has been in my head for days now so who can help me define the best solution?
Thanks!
public abstract class SessionProvider
{
private static SessionProvider _current;
static SessionProvider()
{
_current = new DefaultSessionProvider();
}
public static SessionProvider Current
{
get { return _current; }
set
{
if (value == null)
{
throw new ArgumentNullException();
}
_current = value;
}
}
public abstract string UserName { get; set; }
}
My local default:
public class DefaultSessionProvider : SessionProvider
{
public override string UserName
{
get { return (string) HttpContext.Current.Session["username"]; }
set { HttpContext.Current.Session["username"] = value; }
}
}
So I have access in my entire solution to my SessionProvider, whether this is a real session object or a database-driven storage mechanism...
SessionProvider.Current.UserName = "myUserName";
Once authenticated I want to store a couple of user-related items in
the Session so I don't have to query the database every time to
reconstruct the authenticated user.
Well, it looks like you're working on some sort of caching mechanism. It doesn't really matter if it's in a Session or in Redis cache, or any other type of cache. And this cache is key-value storage. I would create cache interface, something like that:
interface ICache
{
object this[string key] {get; set;}
}
And create concrete classes. SessionCache in your case:
public SessionCache : ICache
{
private IHttpSessionState _session;
public SessionCache(IHttpSessionState session)
{
_session = session;
}
// ICache implementation goes here...
}
So you'll narrow down the problem to dependency-inject Session object to concrete class (SessionCache). With Ninject you can do something like:
.WithConstructorArgument("session",ninjectContext=>HttpContext.Session);
And after that you can finally make your controllers dependent on ICache.
In your unit tests project you can create another ICache concrete class, something like DummyCache with in-memory cache. So you can test your controllers without sticking to Session object.

Sharp architecture; Accessing Validation Results

I am exploring Sharp Architecture and I would like to know how to
access the validation results after calling Entity.IsValid().
I have two scenarios e.g.
1) If the entity.IsValid() return false, I would like to add the
errors to ModelState.AddModelError() collection in my controller.
E.g. in the Northwind sample we have an EmployeesController.Create()
action when we do employee.IsValid(), how can I get access to the
errors?
public ActionResult Create(Employee employee) {
if (ViewData.ModelState.IsValid && employee.IsValid()) {
employeeRepository.SaveOrUpdate(employee);
}
// ....
}
[I already know that when an Action method is called, modelbinder
enforces validation rules(nhibernate validator attributes) as it
parses incoming values and tries to assign them to the model object
and if it can't parse the incoming values  then it register those as
errors in modelstate for each model object property. But what if i
have some custom validation. Thats why we do ModelState.IsValid
first.]
2) In my test methods I would like to test the nhibernate validation
rules as well. I can do entity.IsValid() but that only returns true/
false. I would like to Assert against the actual error not just true/
false.
In my previous projects, I normally use a wrapper Service Layer for
Repositories, and instead of calling Repositories method directly from
controller, controllers call service layer methods which in turn call
repository methods. In my Service Layer all my custom validation rules
resides and Service Layer methods throws a custom exception with a
NameValueCollection of errors which I can easily add to ModelState in
my controller. This way I can also easily implement sophisticated
business rules in my service layer as well. I kow sharp architecture
also provides a Service Layer project. But what I am interested in and
my next question is:
How I can use NHibernate Vaidators to implement sophisticated custom
business rules (not just null,empty, range etc.) and make
Entity.IsValid() to verify those rules too ?
"How I can use NHibernate Vaidators to implement sophisticated custom business rules (not just null,empty, range etc.) and make Entity.IsValid() to verify those rules too ?"
You have to create a custom validation attribute:
here is an example:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class LoginUniqueAttribute : ValidationAttribute
{
private static readonly string DefaultErrorMessage = MUI.LoginNameInUse;
public LoginUniqueAttribute()
: base(DefaultErrorMessage)
{
}
public override string FormatErrorMessage(string name)
{
return DefaultErrorMessage;
}
public override bool IsValid(object value)
{
if (string.IsNullOrEmpty((string)value))
{
return true;
}
var userService = IoC.Resolve<IUserService<User>>();
return userService.GetByLogins(new[] { (string)value }).Count() == 0;
}
}
and usages into a UserInput dto
[Required]
[LoginUniqueAttribute]
[RegularExpression("^[A-Za-z0-9.'\\s]+$", ErrorMessage = "Only characters and digits are allowed")]
[DisplayNameLocalized(typeof(MUI), "LoginName")]
public string LoginName { get; set; }
also do not forget to initialize the validation in your global.asax.cs file on Application_Start:
private void InitializeValidator()
{
var provider = new NHibernateSharedEngineProvider();
NHibernate.Validator.Cfg.Environment.SharedEngineProvider = provider;
}

Validate object based on external factors (ie. data store uniqueness)

Description
My solution has these projects:
DAL = Modified Entity Framework
DTO = Data Transfer objects that are able to validate themselves
BL = Business Layer Services
WEB = presentation Asp.net MVC application
DAL, BL and WEB all reference DTO which is great.
The process usually executes this way:
A web request is made to the WEB
WEB gets DTOs posted
DTOs get automagically validated via custom ActionFilter
validation errors are auto-collected
(Validation is OK) WEB calls into BL providing DTOs
BL calls into DAL by using DTOs (can either pass them through or just use them)
DTO Validation problem then...
My DTOs are able to validate themselves based on their own state (properties' values). But right now I'm presented with a problem when this is not the case. I need them to validate using BL (and consequently DAL).
My real-life example: User registers and WEB gets a User DTO that gets validated. The problematic part is username validation. Its uniqueness should be checked against data store.
How am I supposed to do this?
There's additional info that all DTOs implement an interface (ie. User DTO implements IUser) for IoC purposes and TDD. Both are part of the DTO project.
Impossible tries
I can't reference BL in DTO because I'll get circular reference.
Compilation error
I can't create an additional DTO.Val project that would reference partial DTO classes and implement their validation there (they'd reference BL + DTO).
Partial classes can't span assemblies.
Possible tries
Create a special ActionFilter that would validate object against external conditions. This one would be created within WEB project thus seeing DTO and BL that would be used here.
Put DTOs in BL and keep DTO interfaces as actual DTOs referenced by other projects and refactor all code to use interfaces instead of concrete classes.
Don't handle external dependant validation and let external dependencies throw an exception - probably the worst solution to this issue
What would you suggest?
I would suggest an experiment that i have only been trialling for the last week or so.
Based on this inspiration i am creating DTOs that validate a little differently to that of the DataAnnotations approach. Sample DTO:
public class Contact : DomainBase, IModelObject
{
public int ID { get; set; }
public string Name { get; set; }
public LazyList<ContactDetail> Details { get; set; }
public DateTime Updated { get; set; }
protected override void ConfigureRules()
{
base.AddRule(new ValidationRule()
{
Properties = new string[] { "name" },
Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
validator = () => this.Name.IsRequired300LenNoSpecial()
});
base.AddRule(new ValidationRule()
{
Properties = new string[] { "updated" },
Description = "required",
validator = () => this.Updated.IsRequired()
});
}
}
This might look more work than DataAnnotations and well, that's coz it is, but it's not huge. I think it's more presentable in the class (i have some really ugly DTO classes now with DataAnnotations attributes - you can't even see the properties any more). And the power of anonymous delegates in this application is almost book-worthy (so i'm discovering).
Base class:
public partial class DomainBase : IDataErrorInfo
{
private IList<ValidationRule> _rules = new List<ValidationRule>();
public DomainBase()
{
// populate the _rules collection
this.ConfigureRules();
}
protected virtual void ConfigureRules()
{
// no rules if not overridden
}
protected void AddRule(ValidationRule rule)
{
this._rules.Add(rule);
}
#region IDataErrorInfo Members
public string Error
{
get { return String.Empty; } // Validation should call the indexer so return "" here
} // ..we dont need to support this property.
public string this[string columnName]
{
get
{
// get all the rules that apply to the property being validated
var rulesThatApply = this._rules
.Where(r => r.Properties.Contains(columnName));
// get a list of error messages from the rules
StringBuilder errorMessages = new StringBuilder();
foreach (ValidationRule rule in rulesThatApply)
if (!rule.validator.Invoke()) // if validator returns false then the rule is broken
if (errorMessages.ToString() == String.Empty)
errorMessages.Append(rule.Description);
else
errorMessages.AppendFormat("\r\n{0}", rule.Description);
return errorMessages.ToString();
}
}
#endregion
}
ValidationRule and my validation functions:
public class ValidationRule
{
public string[] Properties { get; set; }
public string Description { get; set; }
public Func<bool> validator { get; set; }
}
/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
#region IsRequired
public static bool IsRequired(this String str)
{
return !str.IsNullOrTrimEmpty();
}
public static bool IsRequired(this int num)
{
return num != 0;
}
public static bool IsRequired(this long num)
{
return num != 0;
}
public static bool IsRequired(this double num)
{
return num != 0;
}
public static bool IsRequired(this Decimal num)
{
return num != 0;
}
public static bool IsRequired(this DateTime date)
{
return date != DateTime.MinValue;
}
#endregion
#region String Lengths
public static bool IsLengthLessThanOrEqual(this String str, int length)
{
return str.Length <= length;
}
public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
{
return !str.IsNullOrTrimEmpty() && (str.Length <= length);
}
public static bool IsRequired300LenNoSpecial(this String str)
{
return !str.IsNullOrTrimEmpty() &&
str.RegexMatch(#"^[- \r\n\\\.!:*,#$%&""?\(\)\w']{1,300}$",
RegexOptions.Multiline) == str;
}
#endregion
}
If my code looks messy well that's because i've only been working on this validation approach for the last few days. I need this idea to meet a few requirements:
I need to support the IDataErrorInfo interface so my MVC layer validates automatically
I need to be able to support complex validation scenarios (the whole point of your question i guess): I want to be able to validate against multiple properties on the same object (ie. StartDate and FinishDate); properties from different/multiple/associated objects like i would have in an object graph; and even other things i haven't thought of yet.
I need to support the idea of an error applying to more than one property
As part of my TDD and DDD journey i want my Domain Objects to describe more my 'domain' than my Service layer methods, so putting these complex conditions in the model objects (not DTOs) seems to achieve this
This approach i think will get me what i want, and maybe you as well.
I'd imagine if you jump on board with me on this that we'd be pretty 'by ourselves' but it might be worth it. I was reading about the new validation capabilities in MVC 2 but it still doesn't meet the above wish list without custom modification.
Hope this helps.
The S#arp Architecture has an [DomainSignature] method identifier that used with the class level validator [HasUniqueDomainSignature] will do the work. See the sample code below:
[HasUniqueDomainSignature]
public class User : Entity
{
public User()
{
}
public User(string login, string email) : this()
{
Login = login;
Email = email;
}
[DomainSignature]
[NotNullNotEmpty]
public virtual string Login { get; set; }
[DomainSignature]
public virtual string Email { get; set; }
}
Take a closer look at http://www.sharparchitecture.net/
I had this exact same problem and after trying to find a work around for days and days and days, I ended up merging my DTO, DAL, and BL into one library. I kept my presentation layer separate.
Not sure if that is an option for you or not. For me, I figured that my chances of ever changing the data store were very slight, and so the separate tier wasn't really needed.
I also have implemented the Microsoft Validation Application Block for all my DTO validations. They have a "Self Validation" method that lets you perform complex validations.
Resulting solution
I ended up using controller action filter that was able to validate object against external factors that can't be obtained from the object itself.
I created the filter that takes the name of the action parameter to check and validator type that will validate that particular parameter. Of course this validator has to implement certain interface to make it all reusable.
[ValidateExternalFactors("user", typeof(UserExternalValidator))]
public ActionResult Create(User user)
validator needs to implement this simple interface
public interface IExternalValidator<T>
{
bool IsValid(T instance);
}
It's a simple and effective solution to a seemingly complex problem.

Questions about the Service Layer as Validation in asp.net mvc

I am a bit confused about the service layer and using it validation.
So I am looking through this tutorial: http://www.asp.net/learn/mvc/tutorial-38-cs.aspx
First if you look at List 3
using System.Collections.Generic;
using System.Web.Mvc;
namespace MvcApplication1.Models
{
public class ProductService : MvcApplication1.Models.IProductService
{
private ModelStateDictionary _modelState;
private IProductRepository _repository;
public ProductService(ModelStateDictionary modelState, IProductRepository repository)
{
_modelState = modelState;
_repository = repository;
}
protected bool ValidateProduct(Product productToValidate)
{
if (productToValidate.Name.Trim().Length == 0)
_modelState.AddModelError("Name", "Name is required.");
if (productToValidate.Description.Trim().Length == 0)
_modelState.AddModelError("Description", "Description is required.");
if (productToValidate.UnitsInStock < 0)
_modelState.AddModelError("UnitsInStock", "Units in stock cannot be less than zero.");
return _modelState.IsValid;
}
public IEnumerable<Product> ListProducts()
{
return _repository.ListProducts();
}
public bool CreateProduct(Product productToCreate)
{
// Validation logic
if (!ValidateProduct(productToCreate))
return false;
// Database logic
try
{
_repository.CreateProduct(productToCreate);
}
catch
{
return false;
}
return true;
}
}
public interface IProductService
{
bool CreateProduct(Product productToCreate);
IEnumerable<Product> ListProducts();
}
}
They same interface just with a different name basically why not just use one?
public interface IProductRepository
{
bool CreateProduct(Product productToCreate);
IEnumerable<Product> ListProducts();
}
public interface IProductService
{
bool CreateProduct(Product productToCreate);
IEnumerable<Product> ListProducts();
}
In my book though(the author who I think wrote this tutorial) has changed it to have IProductRepository to void. So that confuses me even more.
So can someone explain why I need 2 interfaces that seems to do the same thing?
My next questions is my repository has a delete function. Do I put this one in my Service layer too(I guess mandatory if you use one Interface but if you use 2 like about then it could be optinal).
So what would I have in my service layer? Would it just call delete function in the repository? Should it just be a void method or should it return bool? I don't think for this method any validation would need to be done?
So I am not sure if a bool would be needed.
From the tutorial you are reading:
So, application flow control logic
belongs in a controller and data
access logic belongs in a repository.
In that case, where do you put your
validation logic? One option is to
place your validation logic in a
service layer.
A service layer is an additional layer
in an ASP.NET MVC application that
mediates communication between a
controller and repository layer. The
service layer contains business logic.
In particular, it contains validation
logic.
EDIT:
I'm not sure if I can explain it to you in a clear way('cause I'm not fluent in English), but I will try:
A service layer is an additional layer in an ASP.NET MVC application that mediates communication between a controller and repository layer, in that you can handle both validation and application businness. Sometimes you service will need to work with two or more methods of its correspondent repository layer so it doesnt need to have the same interface.
A basic example, let's think you have a register form.
you will have the following interfaces
public interface IUserService
{
bool Register(User mUser);
bool Validate(User mUser);
}
public interface IUserRepository
{
User FindUserByEmail(string Email);
bool Insert(User mUser);
}
so you will end up with two class that will do something like:
public class UserRepository: IUserRepository{
User FindUserByEmail(string Email)
{
//do a ninja search and return an user or null
}
bool Insert(User mUser);
{
//Insert user into db
}
}
public class UserService: IUserService
{
public bool Validate(User mUser)
{
//validate user
}
IUserRepository _respository = new UserRepository();
bool Register(User mUser)
{
if(Validate(mUser);
var hasUser = _respository.FindUserByEmail(User.Email);
if(hasUser==null)
return _respository.Insert(mUser);
return false;
}
}
I think you've made an argument for a single interface in this limited case, but the service and repositories perform two very different functions and you may run into issues down the road if they shared a single interface.
What if the CreateProduct() or ListProducts() needed to have different method signatures in either the service or repository?
What if ValidateProduct() should be defined in the interface? The repository certainly shouldn't have to implement that.
As you've pointed out, there's no need for two interfaces that define the same thing in this particular example, but I assume the author's assumption is that down the road they would be different and therefore necessary.

Session variables in ASP.NET MVC

I am writing a web application that will allow a user to browse to multiple web pages within the website making certain requests. All information that the user inputs will be stored in an object that I created. The problem is that I need this object to be accessed from any part of the website and I don't really know the best way to accomplish this. I know that one solution is to use session variables but I don't know how to use them in asp .net MVC. And where would I declare a session variable? Is there any other way?
I would think you'll want to think about if things really belong in a session state. This is something I find myself doing every now and then and it's a nice strongly typed approach to the whole thing but you should be careful when putting things in the session context. Not everything should be there just because it belongs to some user.
in global.asax hook the OnSessionStart event
void OnSessionStart(...)
{
HttpContext.Current.Session.Add("__MySessionObject", new MySessionObject());
}
From anywhere in code where the HttpContext.Current property != null you can retrive that object. I do this with an extension method.
public static MySessionObject GetMySessionObject(this HttpContext current)
{
return current != null ? (MySessionObject)current.Session["__MySessionObject"] : null;
}
This way you can in code
void OnLoad(...)
{
var sessionObj = HttpContext.Current.GetMySessionObject();
// do something with 'sessionObj'
}
The answer here is correct, I however struggled to implement it in an ASP.NET MVC 3 app. I wanted to access a Session object in a controller and couldn't figure out why I kept on getting a "Instance not set to an instance of an Object error". What I noticed is that in a controller when I tried to access the session by doing the following, I kept on getting that error. This is due to the fact that this.HttpContext is part of the Controller object.
this.Session["blah"]
// or
this.HttpContext.Session["blah"]
However, what I wanted was the HttpContext that's part of the System.Web namespace because this is the one the Answer above suggests to use in Global.asax.cs. So I had to explicitly do the following:
System.Web.HttpContext.Current.Session["blah"]
this helped me, not sure if I did anything that isn't M.O. around here, but I hope it helps someone!
Because I dislike seeing "HTTPContext.Current.Session" about the place, I use a singleton pattern to access session variables, it gives you an easy to access strongly typed bag of data.
[Serializable]
public sealed class SessionSingleton
{
#region Singleton
private const string SESSION_SINGLETON_NAME = "Singleton_502E69E5-668B-E011-951F-00155DF26207";
private SessionSingleton()
{
}
public static SessionSingleton Current
{
get
{
if ( HttpContext.Current.Session[SESSION_SINGLETON_NAME] == null )
{
HttpContext.Current.Session[SESSION_SINGLETON_NAME] = new SessionSingleton();
}
return HttpContext.Current.Session[SESSION_SINGLETON_NAME] as SessionSingleton;
}
}
#endregion
public string SessionVariable { get; set; }
public string SessionVariable2 { get; set; }
// ...
then you can access your data from anywhere:
SessionSingleton.Current.SessionVariable = "Hello, World!";
Well, IMHO..
never reference a Session inside your view/master page
minimize your useage of Session. MVC provides TempData obj for this, which is basically a Session that lives for a single trip to the server.
With regards to #1, I have a strongly typed Master View which has a property to access whatever the Session object represents....in my instance the stongly typed Master View is generic which gives me some flexibility with regards to strongly typed View Pages
ViewMasterPage<AdminViewModel>
AdminViewModel
{
SomeImportantObjectThatWasInSession ImportantObject
}
AdminViewModel<TModel> : AdminViewModel where TModel : class
{
TModel Content
}
and then...
ViewPage<AdminViewModel<U>>
If you are using asp.net mvc, here is a simple way to access the session.
From a Controller:
{Controller}.ControllerContext.HttpContext.Session["{name}"]
From a View:
<%=Session["{name}"] %>
This is definitely not the best way to access your session variables, but it is a direct route. So use it with caution (preferably during rapid prototyping), and use a Wrapper/Container and OnSessionStart when it becomes appropriate.
HTH
Although I don't know about asp.net mvc, but this is what we should do in a normal .net website. It should work for asp.net mvc also.
YourSessionClass obj=Session["key"] as YourSessionClass;
if(obj==null){
obj=new YourSessionClass();
Session["key"]=obj;
}
You would put this inside a method for easy access.
HTH
There are 3 ways to do it.
You can directly access HttpContext.Current.Session
You can Mock HttpContextBase
Create a extension method for HttpContextBase
I prefer 3rd way.This link is good reference.
Get/Set HttpContext Session Methods in BaseController vs Mocking HttpContextBase to create Get/Set methods
My way of accessing sessions is to write a helper class which encapsulates the various field names and their types. I hope this example helps:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.SessionState;
namespace dmkp
{
/// <summary>
/// Encapsulates the session state
/// </summary>
public sealed class LoginInfo
{
private HttpSessionState _session;
public LoginInfo(HttpSessionState session)
{
this._session = session;
}
public string Username
{
get { return (this._session["Username"] ?? string.Empty).ToString(); }
set { this._session["Username"] = value; }
}
public string FullName
{
get { return (this._session["FullName"] ?? string.Empty).ToString(); }
set { this._session["FullName"] = value; }
}
public int ID
{
get { return Convert.ToInt32((this._session["UID"] ?? -1)); }
set { this._session["UID"] = value; }
}
public UserAccess AccessLevel
{
get { return (UserAccess)(this._session["AccessLevel"]); }
set { this._session["AccessLevel"] = value; }
}
}
}
Great answers from the guys but I would caution you against always relying on the Session. It is quick and easy to do so, and of course would work but would not be great in all cicrumstances.
For example if you run into a scenario where your hosting doesn't allow session use, or if you are on a web farm, or in the example of a shared SharePoint application.
If you wanted a different solution you could look at using an IOC Container such as Castle Windsor, creating a provider class as a wrapper and then keeping one instance of your class using the per request or session lifestyle depending on your requirements.
The IOC would ensure that the same instance is returned each time.
More complicated yes, if you need a simple solution just use the session.
Here are some implementation examples below out of interest.
Using this method you could create a provider class along the lines of:
public class CustomClassProvider : ICustomClassProvider
{
public CustomClassProvider(CustomClass customClass)
{
CustomClass = customClass;
}
public string CustomClass { get; private set; }
}
And register it something like:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<ICustomClassProvider>().UsingFactoryMethod(
() => new CustomClassProvider(new CustomClass())).LifestylePerWebRequest());
}
You can use ViewModelBase as base class for all models , this class will take care of pulling data from session
class ViewModelBase
{
public User CurrentUser
{
get { return System.Web.HttpContext.Current.Session["user"] as User };
set
{
System.Web.HttpContext.Current.Session["user"]=value;
}
}
}
You can write a extention method on HttpContextBase to deal with session data
T FromSession<T>(this HttpContextBase context ,string key,Action<T> getFromSource=null)
{
if(context.Session[key]!=null)
{
return (T) context.Session[key];
}
else if(getFromSource!=null)
{
var value = getFromSource();
context.Session[key]=value;
return value;
}
else
return null;
}
Use this like below in controller
User userData = HttpContext.FromSession<User>("userdata",()=> { return user object from service/db });
The second argument is optional it will be used fill session data for that key when value is not present in session.

Resources