Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"An MVC filter provider has already been registered for a different Container instance." in Simple Injector 2.6

I previously had the setup for property injection in one of my attributes as

 Container.RegisterInitializer<PermitAttribute>(initialize =>
 {
     initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
 });

And the usage was

public class PermitAttribute : ActionFilterAttribute
{       
    public IQueryProcessor QueryProcessor { get; set; }
}

but after updating to simpleinjector 2.6.1 The property injection broke. When I am trying to access QueryProcessor object inside PermitAttribute. It resolves null value where as the Simple Injector configuration still has the same property injection via delegate instance .

Is there any breaking change in property injection behavior due to which it was working in v2.5 and its not working anymore in 2.6.1 ?

Update 1:

The Line in the configuration was throwing error for MVC filter provider registration for attributes in v2.6.1

 container.RegisterMvcIntegratedFilterProvider();

For that I commented it . And it stopped the property injection working . The property injection was inside one of my attributes . I guess that's the line above which affects it. And its throwing error in v2.6.1

Update 2:

Message

An MVC filter provider has already been registered for a different Container instance. Registering MVC filter providers for different containers is not supported by this method.

StackTrace :

at SimpleInjector.SimpleInjectorMvcExtensions.RequiresFilterProviderNotRegistered(Container container)
at SimpleInjector.SimpleInjectorMvcExtensions.RegisterMvcIntegratedFilterProvider(Container container)
at RemsPortal.App_Start.SimpleInjectorInitializer.Initialize() in d:\Projects Work\RemsPortal\V2.0 Web Portal\RemsPortal\App_Start\SimpleInjectorInitializer.cs:line 39

Update 3 :

entire Configuration

public static void Initialize()
{
    var container = new Container();

    InitializeContainer(container);
    container.RegisterMvcIntegratedFilterProvider();
    container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

    container.Verify();

    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}

private static void InitializeContainer(Container Container)
{
    Container.RegisterManyForOpenGeneric(typeof(IAsyncCommandHandler<,>),
        AppDomain.CurrentDomain.GetAssemblies());
    Container.RegisterOpenGeneric(typeof(ITransactionCommandHandler<,>),
        typeof(TransactionCommandHandlerDecorator<,>));
    Container.RegisterOpenGeneric(typeof(ICommandResult<>), 
        typeof(CommandHandlerResult<>));
    Container.Register<ICommandResolver, CommandResolver>();

    Container.Register<DbContext, RemsContext>();

    Container.RegisterOpenGeneric(typeof(IPager<>), typeof(PagerModel<>));

    //Container.RegisterPerWebRequest<DbContext, RemsContext>();

    Container.Register<UserManager<Users, Guid>, RemsUserManager>();
    Container.Register<RoleManager<Roles, Guid>, RemsRoleManager>();

    Container.Register<IUserStore<Users, Guid>, 
        UserStore<Users, Roles, Guid, UserLogins, UserRoles, Claims>>();
    Container.Register<IRoleStore<Roles, Guid>, RoleStore<Roles, Guid, UserRoles>>();

    Container.RegisterManyForOpenGeneric(typeof(IAsyncQueryHandler<,>),
        AppDomain.CurrentDomain.GetAssemblies());
    Container.RegisterManyForOpenGeneric(typeof(IAsyncQueryHandler<>),
        AppDomain.CurrentDomain.GetAssemblies());
    Container.RegisterManyForOpenGeneric(typeof(IQueryHandler<,>),
        AppDomain.CurrentDomain.GetAssemblies());
    Container.RegisterOpenGeneric(typeof(IQueryResult<>), typeof(QueryResult<>));

    Container.RegisterOpenGeneric(typeof(IPaginator<>), typeof(Paginator<>));
    Container.Register<IPaginator, Paginator>();

    Container.RegisterOpenGeneric(typeof(IAsyncQueryHandler<>), typeof(BaseQuery<>));
    Container.RegisterOpenGeneric(typeof(IQueryHandler<>), typeof(BaseQuery<>));

    Container.Register<IQueryProcessor, QueryProcessor>(Lifestyle.Singleton);

    Container.Register<ILog, NLogger>(Lifestyle.Singleton);

    Container.RegisterInitializer<PermitAttribute>(initialize =>
    {
        initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
    });

    Container.RegisterInitializer<BaseController>(initialize =>
    {
        initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
        initialize.Logger = Container.GetInstance<ILog>();
    });

    Container.RegisterInitializer<BaseCommandHandler>(initialize =>
    {
        initialize.UserManager = Container.GetInstance<RemsUserManager>();
        initialize.RoleManager = Container.GetInstance<RemsRoleManager>();
        initialize.RemsContext = Container.GetInstance<RemsContext>();
        initialize.QueryProcessor = Container.GetInstance<IQueryProcessor>();
    });

    Container.RegisterInitializer<BaseHandler>(initialize =>
    {
        initialize.UserManager = Container.GetInstance<RemsUserManager>();
        initialize.RolesManager = Container.GetInstance<RemsRoleManager>();
    });
}
like image 636
Joy Avatar asked Dec 20 '22 09:12

Joy


2 Answers

The exception you are seeing is caused by a verification check that has been added to version 2.6 that prevents you from calling RegisterMvcAttributeFilterProvider and RegisterMvcIntegratedFilterProvider multiple times for different container instances. The problem is described in more details here.

The solution is to make sure RegisterMvcIntegratedFilterProvider is called only once in your code for the duration of the complete app domain and since RegisterMvcAttributeFilterProvider is deprecated, prevent having any calls at all to that legacy method. So if you only have one call in there, set a break point on this line, because you might be calling the Initialize() method twice!

The new RegisterMvcIntegratedFilterProvider allows complete integration of MVC attributes in the Simple Injector pipeline which makes sure that the RegisterInitializer method is called on attributes.

Another option though is to enable explicit property injection for attributes, or to fall back on the use of passive attributes as shown here.

But one note on property injection. I noticed you make extensive use of (explicit) property injection, especially for your base classes. From a design perspective however, it's better to remove the base classes all together, because they are a design smell at least, but might become maintenance problems later on. They might violate the Single Responsibility Principle or at least hide that derived types have too many dependencies, which often means too many responsibilities. I create quite big applications myself with MVC and command handlers and query handlers and I am always able to prevent the use of base classes. If a concrete handler needs a dependency, you should simply inject it into the constructor of that type. Prevent hiding that dependency by (ab)using a base type.

There is one important detail that you should be aware about when you use the RegisterMvcIntegratedFilterProvider. MVC caches filter attributes (god knows why) and this means that such attribute is basically becoming a singleton. This implies that every dependency this filter attribute has, becomes a singleton as well. This is of course be big problem if such dependency is not registered as singleton itself; it becomes a captive dependency. Although Simple Injector contains a diagnostic warning to detect these kinds of errors, Simple Injector will be unable to detect this with attributes, because attributes are not registered in the container. Because of this, my advice is to stay away from using property injection in your attributes at all. We are considering to deprecate the RegisterMvcIntegratedFilterProvider method from the MVC integration library.

like image 51
Steven Avatar answered Mar 30 '23 01:03

Steven


As per steven It really was calling the container registration twice .

As I got to see tht

I had called SimpleinjectorInitializer.Initialize(); method in global.asax And then the webactivator also calling the same initizer was taking toll on the simpleinjector which caused the initization to fail for a check .

The solution to that is to remove SimpleinjectorInitializer.Initialize(); from the global.asax and let webactivator do its work .

like image 34
Joy Avatar answered Mar 30 '23 00:03

Joy