I've recently been learning (and struggling with) integrating AutoFac, NHibernate & ASP.NET Web API. I have found several tutorials with code that did not work as anticipated but managed to finally find a working solution.
Essentially what I'm doing is registering a singleton of my SessionFactory
to the WebApiApplication.SessionFactory
object, registering a per-API-request ISession
& IClientRepository
. After these are registered, I'm creating a controller with a DI'ed IClientRepository
(which in turn has an ISession
injected which creates a new session as seen in the code below).
My controllers are marked with a custom TranscationActionFilterAttribute
to bind (or create) a session. The issue I was initially encountering while implementing this pattern is that considering a new session is created every time IClientRepository
was injected with an ISession
, the session was never automatically bound and the code in my Transcation action filter would always create an extraneous session, thereby making updates and deletes not function properly. I got around this by binding the ISession
in the constructor of my ClientRepository
.
Is this safe?
Global.asax:
public class WebApiApplication : System.Web.HttpApplication
{
public static ISessionFactory SessionFactory { get; set; }
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
var configuration = GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
// Register ISessionFactory as Singleton
builder.Register(x => NHibernateConfigurator.BuildSessionFactory()).SingleInstance();
// Register API controller types using assembly scanning.
builder.RegisterAssemblyTypes(
Assembly.GetExecutingAssembly())
.Where(t =>
!t.IsAbstract && typeof(ApiController).IsAssignableFrom(t));
// Register ISession as instance per web request
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession()).InstancePerApiRequest();
// Register clients controller as instance per web request
builder.RegisterType<ClientRepository>().As<IClientRepository>().InstancePerApiRequest();
// Register all controllers
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsSelf();
// Override default dependency resolver to use Autofac
var container = builder.Build();
// Set the dependency resolver implementation.
var resolver = new AutofacWebApiDependencyResolver(container);
configuration.DependencyResolver = resolver;
}
}
TransactionAttribute.cs:
public class TransactionAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
private ISessionFactory SessionFactory { get; set; }
public TransactionAttribute()
{
SessionFactory = WebApiApplication.SessionFactory;
}
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!CurrentSessionContext.HasBind(SessionFactory))
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
}
var session = SessionFactory.GetCurrentSession();
session.BeginTransaction();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = SessionFactory.GetCurrentSession();
var transcation = session.Transaction;
if (transcation != null && transcation.IsActive)
{
transcation.Commit();
}
session = CurrentSessionContext.Unbind(SessionFactory);
session.Close();
}
}
And the constructor for my repository:
public ClientRepository(ISession session)
{
if (session == null) throw new ArgumentNullException("nhSession");
this._session = session;
CurrentSessionContext.Bind(_session);
}