This is a simple ActionFilter we can apply to controls that should reuse the same Unit of Work, Identity map etc between repositories used in an action method:
public class UnitOfWorkFilter : IActionFilter
{
private readonly ISessionContainer _sessionContainer;
public UnitOfWorkFilter(ISessionContainer sessionContainer)
{
Contract.Requires(sessionContainer != null);
_sessionContainer = sessionContainer;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
_sessionContainer.CloseSession();
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_sessionContainer.OpenSession();
}
}
Here is the ISessionContainer interface:
public interface ISessionContainer : IDisposable
{
ISession Session { get; set; }
void OpenSession();
void CloseSession();
}
Here is an implementation of the ISessionContainer that will get and store the nHibernate's ISession into the HttpContext:
public class HttpContextSessionContainer : ISessionContainer
{
private readonly ISessionFactory _sessionFactory;
public HttpContextSessionContainer(ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
public void Dispose()
{
if (Session.IsConnected)
Session.Close();
Session.Dispose();
HttpContext.Current.Items.Remove("NhibSession");
}
public ISession Session
{
get
{
return HttpContext.Current.Items["NhibSession"] as ISession;
}
set
{
HttpContext.Current.Items.Add("NhibSession", value);
}
}
public void OpenSession()
{
Session = _sessionFactory.OpenSession();
}
public void CloseSession()
{
this.Dispose();
}
}
Here is an example of a Controller that have dependencies to two Repositories:
Note: This is only an example so you can see how it can be used, so just dummy code
public class AccountController : Controller
{
public AccountController(IUserRepository users, IRolesRepository roles)
{
Contract.Requires(users != null);
Contract.Requires(roles != null);
_users = users;
_roles = roles;
}
public ActionResult NewUser(UserPM user)
{
_users.Create(...);
_roles.AddUserToRole(...)
return View();
}
...
}
Here is an example of the IUserRepository:
public class UserRepository : BaseRepository<User>, IUserRepository
{
public UserRepository(ISessionContainer sessionContainer) : base(sessionContainer)
{
}
}
The BaseRepository has some CRUD methods, the CRUD methods will get the ISession they need through the ISessionContainer's Session property. If the ISessionContainer's Session is empty, the BaseRepository will make a call to the ISessionCintainer's OpenSession to get an open session to work with, but will close the session when the operation is completed.
When the UnitOfWorkFilter is applied to Controllers, the ActionFilter will call the ISessionContainers's OpenSession method and put the ISession into the HTTPContext when an Action method is executing. The Repositories will then reuse the same ISession. When the execution of an Action method is done, the UnitOfWorkFilter's OnActionExecuted will be called and close the session (It will make sure the ISession.Dispose method is called to clear the ISession to avoid memory leaks etc).
There are other ways of sharing ISession between Repositories etc during an request, for example in the Global.asax, let it put the ISession into the HTTPContext and then in the Application_EndRequest dispose it. We can also create a Base Controller to handle it etc. But I wanted to apply an action filter instead to the Controllers that should reuse an ISession. In my current case, it was the best way (at least what I think).
Fork
0 Feedback
You must log in before you can give any feedback
0
What is the difference to send around ISessionFactory and use ISessionFactory.GetCurrentSession() with the usage of NHibernate.Context.ManagedWebSessionContext in the configuration? Instead of create your own session holder? Of course you need to bind and unbind the session to the context as you do in ActionFilter, but the rest is only inventing the wheel again :).
Posted the same argument on the blog
1
Reply to: NPehrsson
Hi NPehrsson,
Great question. The ISessionContainer is used in different environment in my projects, WCF, Rich Client, Web etc (they all reuse the same Repository detail). The idea of the ISessionContainer is to hide the existence of nHibernate, but the problem is the ISession interface that I reuse from nHibernate, which makes it a leaking abstraction. But the time to replace the ISessionContainer and my code to use Entity Framework instead, takes a few minutes only (Even though, I don't often just replace an OR-mapper in a project). I don't want to pass around a dependency to the ISessionFactory down to the repositories, only the container of a Session that should be crossed shared. The ISessionFactory passed into my current ISessionContainer isn't needed at all, so if I simply make my own ISession instead of reusing the one with nHibernate, I have no dependencies to other frameworks and no leaking abstraction.
0
Reply to: FredrikN
Sounds like a little bit of over design to create your own ISession interface that will be exactly like Nhibernate or EF context for example. And the same on replacing your O/R mapper there isn't that much work IF you want to need that, the problem will more be with the querying and saving than sending around ISessionFactory to some repositories that you could easy use search and replace on. But ok its an argument that some of us may buy and some of us not.
You must log in before you can post a comment


1.25k
0


Mark '.net' tag as 'like'
Mark '.net' tag as 'ignore'