Doing research into the best way to design IRepository<T> structures, I came across a project called 'Whiteboard' (http://whiteboardchat.codeplex.com/) while looking through some forums for NHProf.
I dug around its source code for a while, and found a really interesting attribute for MVC called TransactionAttribute, defined as follows; (I have made brief adjustment to suit my IoC solution)
using System;
using System.Linq;
using Ninject;
namespace System.Web.Mvc
{
/// <summary>
/// This will allow ASP.NET MVC to apply Transactions to the controllers.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class TransactionAttribute : ActionFilterAttribute
{
[Inject]
public NHibernate.ISession Session
{
get;
set;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Session.BeginTransaction();
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (Session.Transaction.IsActive)
{
if (filterContext.Exception == null)
{
Session.Flush();
Session.Transaction.Commit();
}
else
{
Session.Transaction.Rollback();
}
}
}
}
}
This is really interesting; And useful, however something about it bothers me. When I run my queries using NHProf, it gives me warnings about 'Not using transactions properly', and suggests I wrap all queries in a Transaction. Alright, that's fine and good...
So then I go and decorate my Repository<T> : IRepository<T> class like this ...
public T Update(T instance)
{
using (var transaction = session.BeginTransaction())
{
// attempt to perform the given update
session.SaveOrUpdate(instance);
try
{
// commit the transaction to the database
transaction.Commit();
// update succeeded, so we'll return true
return instance;
}
catch
{
// restore the database to its previous state if we failed.
transaction.Rollback();
// update failed, so return a null object
return default(T);
}
}
}
Here's the problem I am running into.
Everywhere I read, the common practice is to always use a Repository for adding to the collections. However the TransactionAttribute, which in itself was brought to my attention by Ayende Rahien's blog, who is from what I can gather one of the primary developers of NHProf, and one of the people working on this Whiteboard project, makes the assumption that you are performing Repository commands at the MVC Controller Level.
So which is it? I'm utterly confused now where my Transaction logic is supposed to go for the best practice. I'm literally finding conflicting answers, and in some cases from the same people.
You are not supposed to deal with transactions inside repositories. A controller (like you have) or HTTP module should start and commit/rollback transactions. Saves or updates are not supposed to be done in isolation. They will be committed at the end of the operation by the controller. This way you can take advantage of ADO batching and other NHibernate features.
Also, make sure to set the FlushMode of the nhibernate ISession to Commit.
Are you decorating your action method or controller class with the [Transaction] attribute? If not, this action filter code won't even be called.
Also, you will need to ensure that you [Inject] the session object into your repository as well and that the session object is scoped to the request.
as an example:
public class MyRepository
{
[Inject]
public ISession Session { get; set; }
public void Save(MyModel model) { Session.Save(model); }
}
public class MyController : Controller
{
[Inject]
public MyRepository MyRepository { get; set; }
[Transaction]
public ActionResult Save(MyModel model)
{
MyRepository.Save(model);
}
}
and when registering your session;
var configuration = new NHibernateConfiguration();
Bind<ISessionFactory>().ToConstant(configuration.GetSessionFactory());
Bind<ISession>().ToMethod(x => x.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
notice the InRequestScope() part
Posting this for #Fatal.
The original response answered my question, but this is inevitably what I ended up doing to avoid using method level attributes.
Instead of declaring the code to control my transaction in an attribute, I included it right in my ISession management for Ninject.
Bind<ISession>()
.ToMethod(c => OpenSession())
.InRequestScope()
.OnActivation(session =>
{
session.BeginTransaction();
session.FlushMode = FlushMode.Commit;
})
.OnDeactivation(session =>
{
if (session.Transaction.IsActive)
{
try
{
session.Transaction.Commit();
}
catch
{
session.Transaction.Rollback();
}
}
});
What this does is open the new ISession each request where an ISession is injected, and when it is activated it begins a new transaction. At this point, Ninject now tracks the state and handles the consequences of rolling it back, thus implementing a very simplistic unit of work pattern.
I do not know if this is the best approach in the world, but I have shown it to a few people and it has not been shot down for bad practice, and it has worked well for me so far.
Related
I'm making a long and detailed post here, but I sincerely believe it's better to provide as much info up front. If any further clarification is needed, please ask. My heartfelt thanks to you for reading my query.
I'm using the book Pro ASP.NET MVC3 Framework by Adam Freeman and Steven Sanderson (Apress, Third Edition), and working through the Sports Store example beginning with Chapter VII. Note that the project entails the use of DI through the use of Ninject, and this is the main reason I wanted to take the time for this walkthrough. Another impressive contribution in this tutorial is the fact that it shows you how to develop an MVC 3 solution into separate projects the way it might be done in in a real world implementation--a thing I've found to be lacking in just about every other book I've ever looked at. (BTW I think the Apress books are just about the best ones out there, generally speaking.)
OK, so I've worked through Chapter 7 and well into Chapter 8, which means that I've successfully created and populated a database and retrieved its contents to appear in my List view. I've also gotten the formatting to work through the .css file. However, there's a catch here: When going through this the first time, I created the database from the IDE itself, which was no problem because there's only one small table (Products) to worry about. Everything was working fine. But then, for reasons I will shortly make clear, I decided to start over. This time, I wanted to follow through the same walkthrough with the exception that I would access the data by attaching to an existing .MDF file rather than creating a new DB from scratch as I coded the project. All this is to pave the way for me to use a similar pattern for my real project, for which I already have built a complex DB, replete with tables, FK relationships, views, functions--and data. I hardly need to say that I want to use my existing database in my "real" project, because recreating all that from scratch would be virtually impossible.
OK, now let's return to the Sports Store example. Try as I might, I cannot get the data from the database to my UI view. There are no error messages at compile time or run time, but instead of seeing the data in the List view, I see nothing but a blank page. Note that in this case it's correct in that there are no column headings or panes because there isn't any scaffolding here.
As far as I can see I have all the parts I need. Let's begin with the Ninject controller factory:
NinjectControllerFactory : DefaultControllerFactory
{
private IKernel ninjectKernel;
public NinjectControllerFactory()
{
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override
IController GetControllerInstance
(RequestContext requestContext, Type controllerType)
{
return controllerType == null ? null
: (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
//Had the mockup here before, which worked fine.
ninjectKernel.Bind<IProductRepository>().To<SportsStore2.Domain.Concrete.EFProductRepository>();
} ...
}
IProductRepository is as follows:
namespace SportsStore2.Domain.Abstract
{
public interface IProductRepository
{
IQueryable<Product> Products { get; }
}
}
And EFProductRepository is implemented thus:
namespace SportsStore2.Domain.Concrete
{
public class EFProductRepository:IProductRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Product> Products
{
get { return context.Products; }
}
}
}
Why yes, of course EFDbContext is defined; viz the declaration:
namespace SportsStore2.Domain.Concrete
{
class EFDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
}
}
And the connection string in Web.config file; this is supposed to wire up the context object definition with the actual database in the .MDF file, at least as I understand it.
<connectionStrings>
<add name="EFDbContext"
connectionString="Data Source=Owner-PC\SQLEXPRESS;Initial Catalog = SportsStore20130205; Integrated security=SSPI"
providerName="System.Data.SqlClient" />
</connectionStrings>
Last and almost certainly least, I have the Product controller defined as follows. I say "least" because I'm almost positive the problem isn't here. In Debug mode, I can tell that the productRepository object isn't being populated. I can see some SQL code in there which is supposed to be run, or have been run by this point. But the invocation of repository.Products returns nothing.
namespace SportsStore2.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository;
public ProductController(IProductRepository productRepository)
{
repository = productRepository;
}
public ViewResult List()
{
return View(repository.Products);
}
public ViewResult Count()
{
int counter = repository.Products.Count();
return View(repository.Products.Count());
}
}
}
namespace SportsStore2.WebUI.Controllers
{
public class ProductController : Controller
{
private IProductRepository repository;
public ProductController(IProductRepository productRepository)
{
repository = productRepository;
}
public ViewResult List()
{
return View(repository.Products);
}
}
}
In order to support lazy loading feature in EF, what is the best way to instantiate DbContext?
I know HttpContext's current item is good place to create DbContext via Application_BeginRequest method and Application_EndRequest method, but in some sample codes of MSDN and official asp.net mvc site, they just create DbContext in Controller's constructor and dispose it in controller's Dispose() method.
I think the both ways are not too different because all of those all implement session per request pattern.
I just want to make sure that my understanding is correct or not.
The Dispose() method in the controller isn't always reliable. By the same token, Session is probably not a good idea either. "Best" is probably subjective, but we've had the best success by using dependency injection (Castle Windsor) and following a Unit of Work Repository pattern.
Setup the unit of work along the following lines:
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork()
{
this.Context = new MyEFEntities();
this.Context.ContextOptions.LazyLoadingEnabled = true;
}
public void Dispose()
{
this.Context.Dispose();
}
public ObjectContext Context { get; internal set; }
}
Setup your repository:
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : class
{
public Repository(IUnitOfWork unitOfWork)
{
Context = unitOfWork.Context;
ObjectSet = Context.CreateObjectSet<TEntity>();
}
public ObjectContext Context { get; set; }
public IObjectSet<TEntity> ObjectSet { get; set; }
}
Register with Castle in Global.asax:
void Application_Start()
{
this.Container.Register(
Component.For<IUnitOfWork>()
.UsingFactoryMethod(() => new UnitOfWork())
.LifeStyle
.Is(LifestyleType.PerWebRequest)
);
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(this.Container));
}
And use in your controller (or wherever you're using it, as long as it's injectable):
public class SomeController
{
public SomeController(IRepository<MyEntity> repository)
{
this.Repository = repository;
}
public IRepository<MyEntity> Repository { get; set; }
public ActionResult MyAction()
{
ViewData.Model = this.Repository.ObjectSet.Single(x => x.Condition); //or something...
}
}
Any lazy loading here could potentially be a trap for a future issue. Without DI, without a repository - its hard to see anything working without it being a hack for lazy loading. Also do you you plan on passing your entities to your view. If so this is going to create a bad overlap. The controller should package data for your view, not have things evaluated later in your view.
For MVC best practices, you should flatten out your domain model as much as possible into a viewmodel (if flattening makes sense) and use the view model. Since you would ideally then know what would be lazy loaded, it may make more sense to take the hit up front and use .Include() in your query to eager load, otherwise you can issue many many queries to the database.
I've used a session factory pattern and saved the DBContext in the session object. It will stay open per session. I haven't had problems with it so far.
Most of my knowledge of ASP.NET MVC 3 comes from reading through the book Pro ASP.NET MVC 3 Framework by Adam Freeman and Steven Senderson. For my test application I have tried to stick to their examples very closely. I am using the repository pattern plus Ninject and Moq which means that unit testing work quite well (i.e. without needing to pull data from the database).
In the book repositories are used like this:
public class EFDbTestChildRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<TestChild> TestChildren
{
get { return context.TestChildren; }
}
public void SaveTestChild(TestChild testChild)
{
if (testChild.TestChildID == 0)
{
context.TestChildren.Add(testChild);
}
else
{
context.Entry(testChild).State = EntityState.Modified;
}
context.SaveChanges();
}
}
And here is the DbContext that goes with it:
public class EFDbContext : DbContext
{
public DbSet<TestParent> TestParents { get; set; }
public DbSet<TestChild> TestChildren { get; set; }
}
Please note: to keep things simple in this extracted example I have left out the interface ITestChildRepository here which Ninject would then use.
In other sources I have seen a more general approach for the repository where one single repository is enough for the whole application. Obviously in my case I end up with quite a list of repositories in my application - basically one for each entity in my domain model. Not sure about the pros and cons about the two approaches - I just followed the book to be on the safe side.
To finally get to my question: each repository has its own DbContext - private EFDbContext context = new EFDbContext();. Do I risk ending up with multiple DbContexts within one request? And would that lead to any significant performance overhead? How about a potential for conflicts between the contexts and any consequences to the data integrity?
Here is an example where I ended up with more than one repository within a controller.
My two database tables are linked with a foreign key relationship. My domain model classes:
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual ICollection<TestChild> TestChildren { get; set; }
}
public class TestChild
{
public int TestChildID { get; set; }
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual TestParent TestParent { get; set; }
}
The web application contains a page that allows the user to create a new TestChild. On it there is a selectbox that contains a list of available TestParents to pick from. This is what my controller looks like:
public class ChildController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult List()
{
return View(testChildRepository.TestChildren);
}
public ViewResult Edit(int testChildID)
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = testChildRepository.TestChildren.First(tc => tc.TestChildID == testChildID);
cvm.TestParents = testParentRepository.TestParents;
return View(cvm);
}
public ViewResult Create()
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = new TestChild();
cvm.TestParents = testParentRepository.TestParents;
return View("Edit", cvm);
}
[HttpPost]
public ActionResult Edit(TestChild testChild)
{
try
{
if (ModelState.IsValid)
{
testChildRepository.SaveTestChild(testChild);
TempData["message"] = string.Format("Changes to test child have been saved: {0} (ID = {1})",
testChild.Name,
testChild.TestChildID);
return RedirectToAction("List");
}
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
// something wrong with the data values
return View(testChild);
}
}
It's not enough to have an EFDbTestChildRepository available but I also need an EFDbTestParentRepository. Both of them are assigned to private variables of the controller - and voila, it seems to me that two DbContexts have been created. Or is that not correct?
To avoid the issue I tried using EFDbTestChildRepository to get to the TestParents. But that obviously will only bring up those that are already hooked up to at least one TestChild - so not what I want.
Here is the code for the view model:
public class ChildViewModel
{
public TestChild TestChild { get; set; }
public IQueryable<TestParent> TestParents { get; set; }
}
Please let me know if I forgot to include some relevant code. Thanks so much for your advice!
There won't be a performance problem (unless we are talking about nanoseconds, instantiating a context is very cheap) and you won't have damaged your data integrity (before that happens you'll get exceptions).
But the approach is very limited and will work only in very simple situations. Multiple contexts will lead to problems in many scenarios. As an example: Suppose you want to create a new child for an existing parent and would try it with the following code:
var parent = parentRepo.TestParents.Single(p => p.Id == 1);
var child = new Child { TestParent = parent };
childrenRepo.SaveTestChild(child);
This simple code won't work because parent is already attached to the context inside of parentRepo but childrenRepo.SaveTestChild will try to attach it to the context inside of childrenRepo which will cause an exception because an entity must not be attached to another context. (Here is actually a workaround because you could set the FK property instead of loading the parent: child.TestParentID = 1. But without a FK property it would be a problem.)
How to solve such a problem?
One approach could be to extend the EFDbTestChildRepository by a new property:
public IQueryable<TestParent> TestParents
{
get { return context.TestParents; }
}
In the example code above you could then use only one repository and the code would work. But as you can see, the name "EFDbTest Child Repository" doesn't really fit anymore to the purpose of the new repository. It should be now "EFDbTest ParentAndChild Repository".
I would call this the Aggregate Root approach which means that you create one repository not for only one entity but for a few entities which are closely related to each other and have navigation properties between them.
An alternative solution is to inject the context into the repositories (instead of creating it in the repositories) to make sure that every repository uses the same context. (The context is often abstracted into a IUnitOfWork interface.) Example:
public class MyController : Controller
{
private readonly MyContext _context;
public MyController()
{
_context = new MyContext();
}
public ActionResult SomeAction(...)
{
var parentRepo = new EFDbTestParentRepository(_context);
var childRepo = new EFDbTestChildRepository(_context);
//...
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
base.Dispose(disposing);
}
}
This gives you a single context per controller you can use in multiple repositories.
The next step might be to create a single context per request by dependency injection, like...
private readonly MyContext _context;
public MyController(MyContext context)
{
_context = context;
}
...and then configuring the IOC container to create a single context instance which gets injected into perhaps multiple controllers.
Do I risk ending up with multiple DbContexts within one request?
Yes. Each instance of a repository is going to instantiate its own DbContexts instances. Depending on the size and use of the application, this may not be a problem although it is not a very scalable approach. There are several ways of handling this though. In my web projects I add the DbContext(s) to the Request's Context.Item collection, this way it is available to all classes that require it. I use Autofac (similar to Ninject) to control what DbContexts are created within specific scenarios and how they are stored, e.g. I have a different 'session manager' for a WCF context to the one for a Http context.
And would that lead to any significant performance overhead?
Yes, but again not massively if the application is relatively small. As it grows though, you may notice the overhead.
How about a potential for conflicts between the contexts and any
consequences to the data integrity?
One of the reasons for using an ORM like this is so that changes can be maintained within the DbContext. If you are instantiating multiple context instances per request you lose this benefit. You wouldn't notice conflicts or any impact of the integrity per se unless you were handling a lot of updates asynchronously.
As promised I post my solution.
I came across your question because I was having trouble with the IIS application pool memory growing beyond limits and having multiple DBContexts was one of my suspects. In retrospect it is fair to say that there were other causes for my trouble. However, it challenged me to find a better layer based design for my repository.
I found this excellent blog: Correct use of Repository and Unit Of Work patterns in ASP.NET MVC leading me to the right direction. The redesign is based on the UnitOfWork pattern. It enables me to have just one constructor parameter for all my controllers instead of "never ending constructor parameters". And after that, I was able to introduce proactive caching as well, which solved a great deal of the earlier mentioned trouble I was having.
Now I only have these classes:
IUnitOfWork
EFUnitOfWork
IGenericRepository
EFGenericRepository
See the referred blog for complete information and implementation of these classes. Just to give an example, IUnitOfWork contains repository definitions for all entities that I need, like:
namespace MyWebApp.Domain.Abstract
{
public interface IUnitOfWork : IDisposable
{
IGenericRepository<AAAAA> AAAAARepository { get; }
IGenericRepository<BBBBB> BBBBBRepository { get; }
IGenericRepository<CCCCC> CCCCCRepository { get; }
IGenericRepository<DDDDD> DDDDDRepository { get; }
// etc.
string Commit();
}
}
The Dependency Injection (DI) is just one statement (I use Ninject):
ninjectKernel.Bind<IUnitOfWork>().To<EFUnitOfWork>();
The Controllers-constructors are maintainable:
public class MyController : BaseController
{
private MyModel mdl = new MyModel();
private IUnitOfWork _context;
public MyController(IUnitOfWork unitOfWork)
{
_context = unitOfWork;
// intialize whatever needs to be exposed to the View:
mdl.whatever = unitOfWork.SomeRepository.AsQueryable();
}
// etc.
Within the Controller I can use _context to access all repositories, if needed. The nice part of it, is that it needs just a single Commit()-call to save changed data for all repositories:
_context.Commit();
I created a web application using ASP.Net MVC 3 and EF 4.1, and I am using the UnitOfWork pattern, but nothing is getting committed to the database. All this is quite new to me, and I don't know where to start to resolve this issue.
I based myself on this post to create my web application:
http://weblogs.asp.net/shijuvarghese/archive/2011/01/06/developing-web-apps-using-asp-net-mvc-3-razor-and-ef-code-first-part-1.aspx
The final code, which can be obtained here also has a service layer and the UnitOfWOrk is being injected into the services.
Instead of using the custom injector based on Unity 2 as they are in that project, I am using Unity.Mvc3.
Here is my IUnitOfWork class:
public interface IUnitOfWork
{
void Commit();
}
And here is my UnitOfWork class:
public class UnitOfWork : IUnitOfWork
{
private readonly IDatabaseFactory databaseFactory;
private MyProjectContext dataContext;
public UnitOfWork(IDatabaseFactory databaseFactory)
{
this.databaseFactory = databaseFactory;
}
protected MyProjectContext DataContext
{
get { return dataContext ?? (dataContext = databaseFactory.Get()); }
}
public void Commit()
{
DataContext.Commit();
}
}
And here is how one of my service class look like:
public class RegionService : IRegionService
{
private readonly IRegionRepository regionRepository;
private readonly IUnitOfWork unitOfWork;
public RegionService(IRegionRepository regionRepository, IUnitOfWork unitOfWork)
{
this.regionRepository = regionRepository;
this.unitOfWork = unitOfWork;
}
...
}
At start-up, my UnitOfWork component is being registered like this:
container.RegisterType<IUnitOfWork, UnitOfWork>();
Now, no matter whether I try to insert, update or delete, no request is being sent to the database. What am my missing here?
UPDATE:
Here is the content of DataContext.Commit():
public class MyProjectContext : DbContext
{
public DbSet<Region> Regions { get; set; }
public virtual void Commit()
{
base.SaveChanges();
}
}
And here is databaseFactory.Get():
public interface IDatabaseFactory : IDisposable
{
MyProjectContext Get();
}
UPDATE #2:
Using the debugger, I am noticing that my Region service and controller constructors are getting called once when performing only a select, but they are called twice when performing an update. Is this normal?
Ok, I found the culprit. It has to do with how I was registering my database factory.
Instead of
container.RegisterType<IDatabaseFactory, DatabaseFactory>();
I needed
container.RegisterType<IDatabaseFactory, DatabaseFactory>(new HierarchicalLifetimeManager());
I found the information on this web site:
http://www.devtrends.co.uk/blog/introducing-the-unity.mvc3-nuget-package-to-reconcile-mvc3-unity-and-idisposable
That's an awfully complex implementation of Unit of Work. I actually prefer this one:
http://azurecoding.net/blogs/brownie/archive/2010/09/22/irepository-lt-t-gt-and-iunitofwork.aspx
Much simpler, and much more flexible. Although you do have to work out a few things for yourself.
May just be a typo but in UnitOfWork your private MyProjectContext is called dataContext (lowercase d)
But in your commit method your calling DataContext.Commit. Any chance that's actually calling a static method that you didn't intend to call? More likely a typo but thought I'd point it out.
+1 for an overly complex implementation of UnitOfWork.
On ASP.NET MVC 2 I have an ActionFilterAttribute called [Transaction] that starts an NHibernate transaction before executing the action and commits or rolls it back afterward, depending on whether or not an exception was thrown. The ISession instance is HttpRequestScoped() and injected by Autofac. It looks like this and works great:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public sealed class TransactionAttribute : ActionFilterAttribute
{
private ITransaction transaction;
public TransactionAttribute()
{
this.Order = 0;
}
public ISession Session
{
get;
set;
}
public override void OnActionExecuted(
ActionExecutedContext filterContext)
{
if (this.Session != null && this.transaction != null)
{
try
{
if (this.transaction.IsActive)
{
if (filterContext.Exception == null)
{
this.transaction.Commit();
}
else
{
this.transaction.Rollback();
}
}
}
finally
{
this.transaction.Dispose();
this.transaction = null;
}
}
}
public override void OnActionExecuting(
ActionExecutingContext filterContext)
{
if (this.Session != null)
{
this.transaction = this.Session.BeginTransaction();
}
}
}
Fantastic. Seems to be a common pattern.
In the ASP.NET MVC 3 notes, I see this little blurb under "Breaking Changes" (emphasis mine):
In previous versions of ASP.NET MVC, action filters were created per request except in a few cases. This behavior was never a guaranteed behavior but merely an implementation detail and the contract for filters was to consider them stateless. In ASP.NET MVC 3, filters are cached more aggressively. Therefore, any custom action filters which improperly store instance state might be broken.
Oops.
Does this mean I'm hosed if I upgrade to MVC 3?
If action filters are no longer instanced per request, how will we get request-scoped dependencies into our action filters?
Thanks for any insight.
I just asked a similar question on google forums.
Here is the link https://groups.google.com/forum/#!topic/autofac/a0qqp2b3WA8
I got the answer:
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterControllers(Assembly.GetExecutingAssembly()).InjectActionInvoker();
Then you can use property injection in your attributes.
Oh yuck.... Nicholas, you might need to store your ISession and Transaction in HttpContext.Items, which you should be able to get to via the ActionExecutedContext/ActionExecutingContext (perhaps setting it in the OnActionExecuting event handler), instead of keeping them in instance members. Either that, or you could call a ServiceLocator inside your filter to grab them for you (also yuck).
Now I have to go look through my MVC 3 code and see if I have any similar problems myself!