我一直在玩ASP.NET MVC RC2中的DI支持。
我已经为NHibernate的每个请求实现了会话,并且需要将ISession
注入我的“工作单元”动作过滤器。
如果我直接引用StructureMap容器(ObjectFactory.GetInstance)或使用DependencyResolver来获取我的会话实例,那么一切正常:
ISession Session {
get { return DependencyResolver.Current.GetService<ISession>(); }
}
但是,如果我尝试使用我的StructureMap
过滤器提供程序(继承FilterAttributeFilterProvider
),我在请求结束时提交NHibernate事务时遇到问题。
就好像在请求之间共享ISession
个对象一样。我经常看到这个,因为我的所有图像都是通过MVC控制器加载的,所以我在正常页面加载时创建了20个左右的NHibernate会话。
我在动作过滤器中添加了以下内容:
ISession Session {
get { return DependencyResolver.Current.GetService<ISession>(); }
}
public ISession SessionTest { get; set; }
public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {
bool sessionsMatch = (this.Session == this.SessionTest);
使用StructureMap过滤器提供程序注入SessionTest。
我发现在包含20张图片的网页上,“sessionsMatch”在2-3次请求中均为false。
我的会话管理的StructureMap配置如下:
For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory());
For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());
在global.asax中,我在每个请求结束时调用以下内容:
public Global() {
EndRequest += (sender, e) => {
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
};
}
这个配置线程安全吗?以前我使用自定义IActionInvoker
将依赖项注入到同一个过滤器中。当我开始遇到上述问题时,这直到MVC 3 RC2正常工作,这就是为什么我认为我会尝试使用过滤器提供程序。
任何帮助都将不胜感激。
我正在使用NHibernate 3 RC和最新版本的StructureMap
更新
以下是DependencyResolver
和FilterAttributeFilterProvider
的实施方式:
public class StructureMapDependencyResolver : IDependencyResolver {
private readonly IContainer container;
public StructureMapDependencyResolver(IContainer container) {
this.container = container;
}
public object GetService(Type serviceType) {
var instance = container.TryGetInstance(serviceType);
if (instance==null && !serviceType.IsAbstract){
instance = AddTypeAndTryGetInstance(serviceType);
}
return instance;
}
private object AddTypeAndTryGetInstance(Type serviceType) {
container.Configure(c=>c.AddType(serviceType,serviceType));
return container.TryGetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType) {
return container.GetAllInstances(serviceType).Cast<object>();
}
}
public class StructureMapFilterAttributeFilterProvider : FilterAttributeFilterProvider
{
private readonly IContainer container;
public StructureMapFilterAttributeFilterProvider(IContainer container) {
this.container = container;
}
protected override IEnumerable<FilterAttribute> GetControllerAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
return BuildUp(base.GetControllerAttributes(controllerContext, actionDescriptor));
}
protected override IEnumerable<FilterAttribute> GetActionAttributes(ControllerContext controllerContext, ActionDescriptor actionDescriptor) {
return BuildUp(base.GetActionAttributes(controllerContext, actionDescriptor));
}
private IEnumerable<FilterAttribute> BuildUp(IEnumerable<FilterAttribute> attributes) {
foreach (var attr in attributes)
container.BuildUp(attr);
return attributes;
}
}
答案 0 :(得分:6)
以为我会回来提供解决方案。
正如@Thomas上面所指出的,Action Filters现在缓存在MVC 3中。这意味着如果你注入一个预期生命周期短的对象(例如http请求),它将会是缓存。
要修复,我们会注入ISession
而不是注入Func<ISession>
。然后每次我们需要访问ISession时,我们都会调用该函数。这样可以确保即使缓存了ActionFilter,也可以正确确定ISession的范围。
我必须像这样配置StructureMap来注入“懒惰”实例(不幸的是它不像Ctor注入那样自动注入一个惰性实例):
x.SetAllProperties(p => {
p.OfType<Func<ISession>>();
});
我更新的ActionFilter如下:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class UnitOfWorkAttribute : ActionFilterAttribute {
public Func<ISession> SessionFinder { get; set; }
public override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
var session = SessionFinder();
session.BeginTransaction();
}
public override void OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext) {
var session = SessionFinder();
var txn = session.Transaction;
if (txn == null || !txn.IsActive) return;
if (filterContext.Exception == null || filterContext.ExceptionHandled)
{
session.Transaction.Commit();
}
else
{
session.Transaction.Rollback();
session.Clear();
}
}
}
答案 1 :(得分:2)
我不知道它是否会有所帮助,但MVC 3动作过滤器现在已缓存,而不是在每个请求开始时实例化为new。因此,如果您在构造函数中注入依赖项,那么它将无法正常工作。你可以发布FilterAttributeFilterProvider的实现吗?
答案 2 :(得分:0)
您是否实现了自己的使用StructureMap的IDependencyResolver?看起来你一定不能拥有,因为你将会话设置为HttpContext范围,但你在同一个请求中看到了不同的会话。