2013-07-28 16 views
1

我试图从Ninject移动到简单的注射器,但我遇到了一个奇怪的问题,当试图复制与Ninject一起使用的功能。简单的注射器FilterInjection似乎是重新初始化RegisterPerWebRequest注入项目

在Ninject我有其中载有服务:

private readonly ICollection<Message> messages; 

此服务被注册为

Bind<INotificationService>().To<NotificationService>() 
    .InRequestScope(); 

该服务允许的邮件(UI和错误)将被传递回MVC网站。

这项服务注入ActionFilterAttribute:

kernel.BindFilter<CriticalErrorAttribute>(FilterScope.Last, 1) 
    .When((context, ad) => 
     !string.IsNullOrEmpty(ad.ActionName) && 
     ad.ControllerDescriptor.ControllerName.ToLower() != "navigation"); 

和内OnActionExecuted使用。

由于该服务已使用InRequestScope注册到Ninject,所以推送到消息队列的所有项目都可在Actionfiter中使用。如果有必要,这允许重定向到错误页面(显示严重错误)。

我试着simpleinjector复制此:

container.RegisterPerWebRequest<INotificationService, NotificationService>(); 

container.RegisterInitializer<CriticalErrorAttribute>(handler => 
{ 
    handler.NotificationService = 
     container.GetInstance<INotificationService>(); 
}); 

注射工作正常,但即使该消息集合包含之前进入ActionFilter的消息,曾经在过滤器的消息集合为空。这就像RegisterPerWebRequest被忽略。

任何帮助解决这个问题,将不胜感激。

回答

3

UPDATE:

简单的喷油器2.5新RegisterMvcIntegratedFilterProvider扩展方法已被添加到MVC集成软件包,它取代了旧RegisterMvcAttributeFilterProvider。这个新的RegisterMvcIntegratedFilterProvider包含下面给出的SimpleInjectorFilterAttributeFilterProvider的行为,并允许将属性更好地集成到Simple Injector流水线中。然而,这确实意味着默认情况下,没有属性被注入,但是可以通过执行custom IPropertySelectionBehavior来扩展。对于旧的RegisterMvcAttributeFilterProvider方法,建议使用新的RegisterMvcIntegratedFilterProvider,该方法将在未来版本中标记为[Obsolete]


当使用RegisterMvcAttributeFilterProvider扩展方法,对MVC属性简单的喷油器不会调用任何注册的初始化。如果您在注入NotificationService的匿名代理中设置断点,您将看到它从未被击中。

简单的喷油器也不过调用container.InjectProperties方法MVC的属性,但InjectProperties确实隐含属性注入,这意味着它试图注入一个类型的所有公共属性,但跳过它,如果财产不能被注入(对于什么原因)。

我打赌CriticalErrorAttribute.NotificationService属性有一个类型NotificationService而不是INotificationService。由于您没有明确注册NotificationService,因此容器将为您创建一个临时实例,这意味着您将为CriticalErrorAttribute获得不同于应用程序其他部分的实例。

快速修复:将属性类型更改为INotificationService

说实话,我很后悔实施了Simple Injector的MVC集成包来使用InjectProperties方法。隐式属性注入是非常邪恶的,因为它在配置错误时不会快速失败,我甚至想在将来取消对InjectProperties的支持。然而,问题在于许多开发人员依赖于InjectProperties。直接通过调用它,或者通过让容器在MVC属性上注入属性来间接实现。

InjectProperties不运行任何初始化程序。这是设计的,还有其他的构造允许对不是由容器创建的对象运行完整的初始化过程。然而问题是,增加这可能会破坏现有的客户端,因为这可能导致属性被多次注入。

在你的情况,我提出一个不同的解决方案:

防止调用您的应用程序的启动路径container.RegisterMvcAttributeFilterProvider()。这将注册一个特殊的FilterAttributeFilterProvider,它在内部调用InjectProperties。你不想使用隐式属性注入,你想要一个更明确的(和完整的)行为。相反,注册以下类:

internal sealed class SimpleInjectorFilterAttributeFilterProvider 
    : FilterAttributeFilterProvider 
{ 
    private readonly ConcurrentDictionary<Type, Registration> registrations = 
     new ConcurrentDictionary<Type, Registration>(); 

    private readonly Func<Type, Registration> registrationFactory; 

    public SimpleInjectorFilterAttributeFilterProvider(Container container) 
     : base(false) 
    { 
     this.registrationFactory = type => 
      Lifestyle.Transient.CreateRegistration(type, container); 
    } 

    public override IEnumerable<Filter> GetFilters(
     ControllerContext context, 
     ActionDescriptor descriptor) 
    { 
     var filters = base.GetFilters(context, descriptor).ToArray(); 

     foreach (var filter in filters) 
     { 
      object instance = filter.Instance; 

      var registration = registrations.GetOrAdd(
       instance.GetType(), this.registrationFactory); 

      registration.InitializeInstance(instance);     
     } 

     return filters; 
    } 
} 

您可以使用下面的代码来注册这个自定义提供:

var filterProvider = 
    new SimpleInjectorFilterAttributeFilterProvider(container); 

container.RegisterSingle<IFilterProvider>(filterProvider); 

var providers = FilterProviders.Providers 
    .OfType<FilterAttributeFilterProvider>().ToList(); 

providers.ForEach(provider => FilterProviders.Providers.Remove(provider)); 

FilterProviders.Providers.Add(filterProvider); 

这种习俗SimpleInjectorFilterAttributeFilterProvider调用Registration.InitializeInstance方法。此方法允许初始化一个已经创建的类型,并通过调用类型初始化代理来初始化它(除其他外)。

有关使用属性的更多信息,请阅读following discussion

+1

你也可以实现一个自定义的IPropertySelectionBehavior来允许Simple Injector强制在属性上注入属性。这可以防止您必须编写自定义初始化程序。如果可以的话,evn明确注册属性。这样他们可以被验证。 – Steven

+0

应该是'FilterProviders.Providers.Add(filterProvider);'? – ajbeaven

+1

@ajbeaven:谢谢。我甚至在'SimpleInjectorFilterAttributeFilterProvider'中修复了一个更重要的bug,这使得它很不可用。 – Steven