使用 StructureMap 在 ASP.NET MVC 3 RC2 中进行操作过滤器依赖注入
我一直在研究 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 Filter 提供程序注入 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;
}
}
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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您是否实现了自己的使用 StructureMap 的 IDependencyResolver?看起来您一定没有,因为您将会话设置为 HttpContext 范围,但您在同一请求期间看到了单独的会话。
Did you implement your own IDependencyResolver that uses StructureMap? It seems like you must not have, because you set the session to be HttpContext scoped and yet you are seeing separate sessions during the same request.
我想我会回来提供解决方案。
正如 @Thomas 上面指出的,动作过滤器现在缓存在 MVC 3 中。这意味着,如果您注入一个预期生命周期较短的对象(例如 http 请求),它将被缓存。
要修复此问题,我们不注入
ISession
,而是注入Func
。然后每次我们需要访问 ISession 时我们都会调用该函数。这确保了即使 ActionFilter 被缓存,ISession 的作用域也正确。我必须像这样配置 StructureMap 来注入“惰性”实例(不幸的是,它不会像 Ctor 注入那样自动注入惰性实例):
我更新的 ActionFilter 如下:
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 aFunc<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):
My updated ActionFilter is below:
我不知道这是否有帮助,但 MVC 3 操作过滤器现在被缓存,而不是在每个请求开始时重新实例化。因此,如果您在构造函数中注入依赖项,它将无法正常工作。您可以发布 FilterAttributeFilterProvider 的实现吗?
I don't know if it would help but with MVC 3 action filters are now cached instead of being instantiated new at the beginning of every request. So if you're injecting dependencies something in the constructor it won't work well. Could you post your implementation of FilterAttributeFilterProvider ?