简单的喷油器FilterInjection似乎重新初始化RegisterPerWebRequest注入项目 [英] Simple Injector FilterInjection seems to be reinitialising RegisterPerWebRequest injected item

查看:259
本文介绍了简单的喷油器FilterInjection似乎重新初始化RegisterPerWebRequest注入项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图从Ninject移到简单喷油器,但我遇到试图复制与Ninject工作功能时,一个奇怪的问题。



在Ninject我有其中载有服务:

 私人只读的ICollection<消息>消息; 

本服务被注册为



 绑定< INotificationService方式>()为<&NotificationService GT;()
.InRequestScope();

本服务允许被传递回MVC网站消息(UI和错误)。



本服务注入了ActionFilterAttribute:

  kernel.BindFilter< CriticalErrorAttribute> (FilterScope.Last,1)
。当((上下文,广告)= GT;!
string.IsNullOrEmpty(ad.ActionName)及&放大器;
ad.ControllerDescriptor.ControllerName.ToLower ()=导航)!;

和内OnActionExecuted使用。



由于该服务被注册到Ninject与InRequestScope,任何产品推到消息队列中Actionfiter可用。这允许重定向到一个错误页面(如有必要,显示关键错误)



我试着simpleinjector重复这样的:

  container.RegisterPerWebRequest< INotificationService,NotificationService>(); 

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



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



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


解决方案

更新:



在简单的喷油器2.5新 RegisterMvcIntegratedFilterProvider 扩展方法已经被添加到MVC集成包取代了旧的 RegisterMvcAttributeFilterProvider 。这种新的 RegisterMvcIntegratedFilterProvider 包含行为的 SimpleInjectorFilterAttributeFilterProvider 即如下,并允许更好的集成属性到简单的喷油器管道。然而,这并不意味着在默认情况下,没有属性被注入,但可以通过实施的自定义IPropertySelectionBehavior 的。采用新的 RegisterMvcIntegratedFilterProvider 被劝了旧 RegisterMvcAttributeFilterProvider 方法,该方法将被标记 [过时] 在未来的版本。






在使用 RegisterMvcAttributeFilterProvider 扩展方法,对MVC属性简单喷油器不会叫任何注册的初始化。如果设置了注入的 NotificationService 你会看到它从不打匿名委托内部的一个破发点。



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



我敢打赌, CriticalErrorAttribute.NotificationService 属性都有一个类型 NotificationService 而不是 INotificationService 。既然你没有注册 NotificationService 明确,容器会为你创建一个瞬态的实例,这意味着你会得到一个不同的实例为 CriticalErrorAttribute 比应用程序的其它部分也越来越



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



说实话,我很后悔曾经实现了MVC集成包简单的注射器使用 InjectProperties 方法。隐式属性注入是非常邪恶的,因为它没有快速失败时,有一个配置错误,我甚至考虑在未来消除对 InjectProperties 支持。问题是,这不过许多开发商都依赖于 InjectProperties 。无论是直接调用它,或间接通过让MVC的属性容器注入的属性。



InjectProperties 不运行任何初始化。这是通过设计,并且有允许运行在未由容器创建的对象的完全初始化过程中的其他构建体。 。问题是,但是,加入这可能会破坏现有的客户,因为这可能会导致被注射多次的属性。



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



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



<预类=郎-CS prettyprint-覆盖> 内部密封类SimpleInjectorFilterAttributeFilterProvider
:FilterAttributeFilterProvider
{
私人只读ConcurrentDictionary<类型,登记>注册=
新ConcurrentDictionary<类型,登记>();

私人只读Func键<类型,登记> registrationFactory;

公共SimpleInjectorFilterAttributeFilterProvider(集装箱货柜)
:基地(假)
{
this.registrationFactory =类型=>
Lifestyle.Transient.CreateRegistration(类型,容器);
}

公共覆盖的IEnumerable<滤光器> GetFilters(
ControllerContext背景下,
ActionDescriptor描述符)
{
VAR过滤器= base.GetFilters(背景下,描述符).ToArray();

的foreach(在过滤器变种过滤器)
{
对象实例= filter.Instance;

VAR注册= registrations.GetOrAdd(
instance.GetType(),this.registrationFactory);

registration.InitializeInstance(实例);
}

返回过滤器;
}
}

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



<预类=郎-CS prettyprint-覆盖> VAR filterProvider =
新SimpleInjectorFilterAttributeFilterProvider(容器);

container.RegisterSingle< IFilterProvider>(filterProvider);

变种提供商= FilterProviders.Providers
.OfType&所述; FilterAttributeFilterProvider方式>()了ToList();

providers.ForEach(供应商=> FilterProviders.Providers.Remove(供应商));

FilterProviders.Providers.Add(filterProvider);

这习俗 SimpleInjectorFilterAttributeFilterProvider 调用的 Registration.InitializeInstance 方法。这种方法允许初始化一个已经创建,将通过(其中包括)调用类型初始化代表初始化一个类型。



有关使用属性的更多信息,请阅读下面的讨论


I'm trying to move from Ninject to Simple Injector but I'm experiencing an odd issue when trying to duplicate functionality that worked with Ninject.

In Ninject I had a service which contained:

private readonly ICollection<Message> messages;

This service was registered as

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

This service allowed messages (UI and error) to be passed back to the MVC site.

This service was injected into an ActionFilterAttribute:

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

and used within OnActionExecuted.

Because the service was registered to Ninject with InRequestScope, any items pushed to the message queue were available in the Actionfiter. This allowed for a redirect to an error page (displaying critical errors) if necessary.

I've tried to duplicate this with simpleinjector:

container.RegisterPerWebRequest<INotificationService, NotificationService>();

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

The injection is working fine, but even though the message collection contains messages prior to entering the ActionFilter, once in the filter the message collection is empty. It's like the RegisterPerWebRequest is being ignored.

Any help in solving this issues would be appreciated.

解决方案

UPDATE:

In Simple Injector 2.5 a new RegisterMvcIntegratedFilterProvider extension method has been added to the MVC Integration package that replaces the old RegisterMvcAttributeFilterProvider. This new RegisterMvcIntegratedFilterProvider contains the behavior of the SimpleInjectorFilterAttributeFilterProvider that is given below and allows better integration of attributes into the Simple Injector pipeline. This does mean however that by default, no properties are injected, but this can extended by implementing a custom IPropertySelectionBehavior. The use of the new RegisterMvcIntegratedFilterProvider is adviced over the old RegisterMvcAttributeFilterProvider method, which will be marked [Obsolete] in a future release.


When using the RegisterMvcAttributeFilterProvider extension method, Simple Injector will not call any registered initializer on MVC attributes. If you set a break point inside the anonymous delegate that injects the NotificationService you'll see it's never hit.

Simple Injector does however call the container.InjectProperties method on MVC attributes, but InjectProperties does implicit property injection, which means that it tries to inject all public properties on a type, but skips it if the property can't be injected (for what ever reason).

I bet the CriticalErrorAttribute.NotificationService property has a type of NotificationService instead of INotificationService. Since you didn't register NotificationService explicitly, the container will create a transient instance for you, which means you'll get a different instance for the CriticalErrorAttribute than the rest of the application is getting.

Quick fix: change the property type to INotificationService.

To be honest, I regret ever implemented the MVC integration package for Simple Injector to use the InjectProperties method. Implicit Property injection is very evil, because it doesn't fail fast when there's a misconfiguration and I'm even thinking about removing support for InjectProperties in the future. The problem is however that many developers are depending on InjectProperties. Either directly by calling it, or indirectly by letting the container inject properties on MVC attributes.

InjectProperties does not run any initializer. That's by design, and there are other constructs that allow running the full initialization process on objects that are not created by the container. Problem is however, that adding this could break existing clients, since this could result in properties being injected multiple times.

In your case, I suggest a different solution:

Prevent calling container.RegisterMvcAttributeFilterProvider() in the startup path of your application. This will register a special FilterAttributeFilterProvider that calls InjectProperties internally. You don't want to use implicit property injection, you want a more explicit (and complete) behavior. Instead register the following class:

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;
    }
}

You can use the following code to register this custom provider:

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);

This custom SimpleInjectorFilterAttributeFilterProvider calls the Registration.InitializeInstance method. This method allows initialization a type that is already created and will initialize it by (among other things) calling the type initializer delegates.

For more information about working with attributes, please read the following discussion.

这篇关于简单的喷油器FilterInjection似乎重新初始化RegisterPerWebRequest注入项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆