问题描述
我一直在玩的ASP.NET MVC RC2的DI支持。
我已经实现了每个请求会话NHibernate和需要注入的Isession
进入我的工作单位行动过滤器。
如果我引用StructureMap容器直接(ObjectFactory.GetInstance)或使用DependencyResolver让我的会话实例,一切工作正常:
的Isession会话{
{返回DependencyResolver.Current.GetService<&ISession的GT;(); }
}
但是,如果我尝试使用我的 StructureMap
过滤器供应商(继承 FilterAttributeFilterProvider
)我有犯NHibernate的问题交易在请求的结束。
这是因为如果的Isession
正在请求之间共享对象。我经常看到这是我所有的图像都通过一个MVC控制器中加载的,所以我得到一个正常的页面加载创建20个左右NHibernate的会议。
我添加以下到我的行为过滤器:
的Isession会话{
{返回DependencyResolver.Current.GetService<&ISession的GT;(); }
} 公众的ISession SessionTest {搞定;组; } 公共覆盖无效OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext){ 布尔sessionsMatch =(this.Session == this.SessionTest);
SessionTest使用StructureMap过滤提供商注入。
我发现,在页面上有20个图像,sessionsMatch为要求2-3是假的。
有会话管理我StructureMap配置如下:
对少于ISessionFactory方式>()辛格尔顿()使用(新NHibernateSessionFactory()提供了getSessionFactory());
对于< ISession的方式>()HttpContextScoped()使用。(CTX => ctx.GetInstance< ISessionFactory方式>()的openSession());
在我的Global.asax调用在每个请求的末尾以下内容:
公共全球(){
EndRequest + =(发件人,E)=> {
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
};
}
这是配置线程安全的? previously我使用的是自定义的 IActionInvoker
注入依赖到同一个过滤器。这种精细的工作,直到MVC 3 RC2当我开始遇到上述问题,这就是为什么我想我会尝试使用过滤器提供商,而不是
任何帮助将是AP preciated。
我使用NHibernate的3 RC和StructureMap的最新版本
更新:
下面是我的实现 DependencyResolver
和 FilterAttributeFilterProvider
:
公共类StructureMapDependencyResolver:{的IDependencyResolver
私人只读的IContainer容器; 公共StructureMapDependencyResolver(集装箱的IContainer){
this.container =容器;
} 公共对象GetService的(类型的serviceType){
VAR实例= container.TryGetInstance(的serviceType);
如果(例如== NULL和放大器;&安培;!serviceType.IsAbstract){
例如= AddTypeAndTryGetInstance(的serviceType);
}
返回实例;
} 私有对象AddTypeAndTryGetInstance(类型的serviceType){
container.Configure(C => c.AddType(的serviceType,的serviceType));
返回container.TryGetInstance(的serviceType);
} 公共IEnumerable的<对象> GetServices(类型的serviceType){
返回container.GetAllInstances(的serviceType).Cast<对象>();
}
}
公共类StructureMapFilterAttributeFilterProvider:FilterAttributeFilterProvider
{
私人只读的IContainer容器; 公共StructureMapFilterAttributeFilterProvider(集装箱的IContainer){
this.container =容器;
} 保护覆盖的IEnumerable< FilterAttribute> GetControllerAttributes(ControllerContext controllerContext,ActionDescriptor actionDescriptor){
累积回报(base.GetControllerAttributes(controllerContext,actionDescriptor));
} 保护覆盖的IEnumerable< FilterAttribute> GetActionAttributes(ControllerContext controllerContext,ActionDescriptor actionDescriptor){
累积回报(base.GetActionAttributes(controllerContext,actionDescriptor));
} 私人的IEnumerable< FilterAttribute>建设(IEnumerable的< FilterAttribute>属性){
的foreach(在属性VAR ATTR)
container.BuildUp(attr)使用;
返回属性;
}
}
想我会回来,并提供了解决方案。
由于@Thomas上文所指出的,措施筛选器现在被高速缓存在MVC 3,这意味着,如果你注入用的意的寿命短的时间(如http请求),这将是一个对象缓存。
要解决,而不是在注入的Isession
我们注入 Func键<&ISession的GT;
。然后,每次我们需要访问ISession的时候,我们调用函数。这确保了即使ActionFilter是缓存,所述的ISession正确作用域
我不得不配置StructureMap像这样注入的懒的实例(遗憾的是它并没有注入一个懒惰的实例会自动喜欢它与男星的注射没有):
x.SetAllProperties(P => {
p.OfType<&Func键LT;&ISession的GT;>();
});
我的更新ActionFilter是如下:
[AttributeUsage(AttributeTargets.Method,的AllowMultiple = TRUE)]
公共类UnitOfWorkAttribute:ActionFilterAttribute { 公共Func键<&ISession的GT; SessionFinder {搞定;组; } 公共覆盖无效OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext){
VAR会话= SessionFinder();
session.BeginTransaction();
} 公共覆盖无效OnResultExecuted(System.Web.Mvc.ResultExecutedContext filterContext){
VAR会话= SessionFinder(); VAR TXN = session.Transaction; 如果(TXN == NULL || txn.IsActive!)回报; 如果(filterContext.Exception == NULL || filterContext.ExceptionHandled)
{
session.Transaction.Commit();
}
其他
{
session.Transaction.Rollback();
session.Clear();
}
}
}
I've been playing with the DI support in ASP.NET MVC RC2.
I have implemented session per request for NHibernate and need to inject ISession
into my "Unit of work" action filter.
If I reference the StructureMap container directly (ObjectFactory.GetInstance) or use DependencyResolver to get my session instance, everything works fine:
ISession Session {
get { return DependencyResolver.Current.GetService<ISession>(); }
}
However if I attempt to use my StructureMap
filter provider (inherits FilterAttributeFilterProvider
) I have problems with committing the NHibernate transaction at the end of the request.
It is as if ISession
objects are being shared between requests. I am seeing this frequently as all my images are loaded via an MVC controller so I get 20 or so NHibernate sessions created on a normal page load.
I added the following to my action filter:
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);
SessionTest is injected using the StructureMap Filter provider.
I found that on a page with 20 images, "sessionsMatch" was false for 2-3 of the requests.
My StructureMap configuration for session management is as follows:
For<ISessionFactory>().Singleton().Use(new NHibernateSessionFactory().GetSessionFactory());
For<ISession>().HttpContextScoped().Use(ctx => ctx.GetInstance<ISessionFactory>().OpenSession());
In global.asax I call the following at the end of each request:
public Global() {
EndRequest += (sender, e) => {
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
};
}
Is this configuration thread safe? Previously I was injecting dependencies into the same filter using a custom IActionInvoker
. This worked fine until MVC 3 RC2 when I started experiencing the problem above, which is why I thought I would try using a filter provider instead.
Any help would be appreciated.
I'm using NHibernate 3 RC and the latest version of StructureMap
Update:
Below are my implementations of DependencyResolver
and 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;
}
}
Thought I would come back and provide the solution.
As @Thomas pointed out above, Action Filters are now cached in MVC 3. This means that if you inject an object with an intended short life time (e.g. http request), it's going to be cached.
To fix, instead of injecting an ISession
we inject a Func<ISession>
. Then each time we need access to ISession we invoke the function. This ensures that even if the ActionFilter is cached, the ISession is scoped correctly.
I had to configure StructureMap like so to inject the "lazy" instance (unfortunately it doesn't inject a lazy instance automatically like it does with Ctor injection):
x.SetAllProperties(p => {
p.OfType<Func<ISession>>();
});
My updated ActionFilter is below:
[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();
}
}
}
这篇关于在ASP.NET MVC 3 RC2与StructureMap操作过滤器依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!