A while back i ranted about an architectural review I had to do. This post comes to you directly after another trip through that very code base. You can find the code for this post here.
The framework is built around the Page controller pattern, each page has a dedicated controller.
This controller uses "services", which can be in proc destinations (logging and tracing) or true services (wcf, asmx).
Each service has a caching level (Application, Session, Request) which - in a web context - points to respectively AppDomain.Current.Data, HttpSession items and HttpContext items. On a conceptual level, this is ok for me...
I have some problems however, with the implementation of said idea's, which don't really need to be listed here.
So, the idea for this post was to see if I can implement the same functionality (and more) using Unity.
Life Time Managers
So the first step in the trial is to create the life time manager classes that point to the HttpSession and HttpContext. If you've been paying attention, the third caching level (Application ) basically means that you have a singleton, which is already provided by the ContainerControlledLifeTimeManager.
It turns out that writing a custom LifeTimeManager isn't that hard!
A lifetime manager is in charge of saving a reference to a type instance, returning null if the instance has not yet been registered.
The issue with HttpContext and HttpSession is that these are dictionaries, not just lists - so you'll need a key to save the service instance. This is why I made the two managers generic...
ContextLifeTimeManager
public class ContextLifetimeManager<T> : LifetimeManager, IDisposable
{
public override object GetValue()
{
return HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
}
public override void RemoveValue()
{
HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Items[typeof(T).AssemblyQualifiedName] = newValue;
}
public void Dispose()
{
RemoveValue();
}
}
SessionLifetimeManager
public class SessionLifetimeManager<T> : LifetimeManager, IDisposable
{
public override object GetValue()
{
return HttpContext.Current.Session[typeof(T).AssemblyQualifiedName];
}
public override void RemoveValue()
{
HttpContext.Current.Session.Remove(typeof(T).AssemblyQualifiedName);
}
public override void SetValue(object newValue)
{
HttpContext.Current.Session[typeof(T).AssemblyQualifiedName] = newValue;
}
public void Dispose()
{
RemoveValue();
}
}
Web.Library
Using Unity in a web application has some consequences, as the web programming model is quite different from the client programming model.
The next hurdle I needed to take was being able to access a central Unity Container from within every page - resolving the Controller type for that specific page.
To this end I created a few Utility classes:
- Web.Library.Application
class inheriting from the "normal" System.Web.HttpApplication, exposing an extra UnityContainer. - Web.Library.Page<TController>
public class Page<TController> : System.Web.UI.Page
{
protected override void OnInitComplete(EventArgs e)
{
base.OnInitComplete(e);
Resolve();
}
protected virtual void Resolve()
{
Controller = Web.Library.Application.Container.Resolve<TController>();
}
public TController Controller { get; private set; }
}
This actually caused me a nice bit of trouble. In the initial version I put the Resolve code into the constructor, which works quite well, except with the SessionLifeTimeManager. The problem here is that the Page is created long before the SessionState is retrieved, so you get an "object not set to a reference" on the HttpContext.Current.Session..
Moving the instantiation to OnInitComplete solves the problem completely!
DummyApplication - a usage example
The DummyApplication consits out of two projects
DummyApplication.Library
Class libary project containing the pagecontrollers, services (interfaces + implementations) and the model.
Of note here is that service implementations have but one constructor, so that one is automatically chosen by the unity container.
DummyApplication.Web
A basic web project, containing the views (web pages).
Of note here is that the web pages inherit from the Web.Library.Page<TController> and the global asax inherits from Web.Library.Application. The configuration of the container is done via the Application_start method (could also be doen via configuration):
protected void Application_Start(object sender, EventArgs e)
{
Container = new UnityContainer();
//logger is registerd as a singleton, one logger for the entire applciation
Container.RegisterType<ILog, Logger>(new ContainerControlledLifetimeManager());
//customer service is registed as per session
Container.RegisterType<ICustomerService, CustomerService>(new SessionLifetimeManager<ICustomerService>());
// controllers are per request
Container.RegisterType<CustomerListController>(new ContextLifetimeManager<CustomerListController>());
Container.RegisterType<CustomerDetailController>(new ContextLifetimeManager<CustomerDetailController>());
}
UnityData.ascx
Also in the Web application is a UnityData usercontrol, which shows the id's of the different services and controllers, which allows you to test the object creation :)
Web application scenario
The web app shows you two pages, a list and a detail. The list page returns a (hard coded list) and can forward you to the detail. The detail page shows you the customer detail, but the change isn't implemented (hey it's a demo !)
Conclusions
This little demo project shows that it is possible to use unity to create a *clean* implementation of service caching.
As always, comments and feedback are encouraged
4 comments:
Great Job! Realy useful samples. It is great to use in Web Application utilizng MVP pattern. When different lifetime of objects are need it. For instance I am using controllers for managing page-flow and keeping state of the current flow. Now I can use DI for controllers, instead of passing them to User controls. Thank you!
Great Job! Realy useful samples. It is great to use in Web Application utilizng MVP pattern. When different lifetime of objects are need it. For instance I am using controllers for managing page-flow and keeping state of the current flow. Now I can use DI for controllers, instead of passing them to User controls. Thank you!
Hi! Your blog is simply super. you have create a differentiate. Thanks for the sharing this website. it is very useful professional knowledge. Great idea you know about company background.
Customized application development
Simply fantastic overview of implementing Unity in ASP.NET MVC, and the sample solution itself is nicely factored as well. Many thanks!
Post a Comment