Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AddPageModelActivation() can't resolve Microsoft-specified types like LogoutModel<IdentityUser> #728

Open
oleg-gochachko opened this issue Jun 24, 2019 · 1 comment

Comments

Projects
None yet
2 participants
@oleg-gochachko
Copy link

commented Jun 24, 2019

I follow https://simpleinjector.org/aspnetcore guide to integrate SI to my web site (standard template with authentication).

However, when I try to open login page - I recieve the folloving error:

System.InvalidOperationException: For the SimpleInjectorPageModelActivatorProvider to function properly, it requires all page models to be registered explicitly in Simple Injector, but a registration for LogoutModel<IdentityUser> is missing. To ensure all page models are registered properly, call the RegisterPageModels extension method on the Container from within your Startup.Configure method while supplying the IApplicationBuilder instance, e.g. "this.container.RegisterPageModels(app);".
Full page model name: Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.LogoutModel`1[[Microsoft.AspNetCore.Identity.IdentityUser, Microsoft.Extensions.Identity.Stores, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].

Stack trace:

at SimpleInjector.Integration.AspNetCore.Mvc.SimpleInjectorPageModelActivatorProvider.CreateActivator(CompiledPageActionDescriptor descriptor)
at Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.DefaultPageModelFactoryProvider.CreateModelFactory(CompiledPageActionDescriptor descriptor)
at Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvokerProvider.CreateCacheEntry(ActionInvokerProviderContext context, FilterItem[] cachedFilters)
at Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvokerProvider.OnProvidersExecuting(ActionInvokerProviderContext context)
at Microsoft.AspNetCore.Mvc.Internal.ActionInvokerFactory.CreateInvoker(ActionContext actionContext)
at Microsoft.AspNetCore.Mvc.Internal.MvcEndpointDataSource.<>c__DisplayClass21_0.<CreateEndpoint>b__0(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

After several hours of tries I'm able to start the project propery by using these configurations:

// add simple injector ...
services.AddSimpleInjector(Container, options =>
{
    // AddAspNetCore() wraps web requests in a Simple Injector scope.
    options.AddAspNetCore()
        // Ensure activation of a specific framework type to be created by
        // Simple Injector instead of the built-in configuration system.
        .AddControllerActivation()
        .AddViewComponentActivation()
        //.AddPageModelActivation()
        .AddTagHelperActivation();
});            

services.EnableSimpleInjectorCrossWiring(Container);            
services.AddSingleton<IPageModelActivatorProvider>(
    new SimpleInjectorPageModelActivatorProvider(Container));

and

app.UseSimpleInjector(Container, options =>
{
    //options.AutoCrossWireFrameworkComponents = false;

    // Add custom Simple Injector-created middleware to the ASP.NET pipeline.
    //options.UseMiddleware<CustomMiddleware1>(app);
    //options.UseMiddleware<CustomMiddleware2>(app);

    // Optionally, allow application components to depend on the
    // non-generic Microsoft.Extensions.Logging.ILogger abstraction.
    //options.UseLogging();
});

Container.RegisterPageModels(app);            
Container.AutoCrossWireAspNetComponents(app);

As you may see method .AddPageModelActivation() commented and obsolete method RegisterPageModels(app); and services.AddSingleton<IPageModelActivatorProvider> added.

I think AddPageModelActivation not enumerate some of Identity view models and should be fixed.

@dotnetjunkie

This comment has been minimized.

Copy link
Collaborator

commented Jun 24, 2019

Thank you for reporting this issue. This part of the ASP.NET Core stack is still something that might need improvement.

Your issue is a bit tricky, because there are a few things going on. First of all, the resolved type is a generic type defined internally inside the Microsoft libraries. That's why it hasn't been registered by Simple Injector. It only registers concrete, non-generic types that are part of application parts of type IApplicationPartTypeProvider.

The SimpleInjectorPageModelActivatorProvider requires types to be registered explicitly, which is similar to what the built-in MS.DI container requires. In the case of Page Models, however, Microsoft seems to circumvent that default behavior and circumvents going through the built-in container. Instead, the built-in IPageModelActivatorProvider has its own DI mechanism and only resolves Page Model's dependencies from the built-in container.

This is why the built-in behavior can resolve an Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.LogoutModel<Microsoft.AspNetCore.Identity.IdentityUser>. The Simple Injector implementation, however, checks to see whether the type is registered. If this is not the case, it will fall back to using the built-in MS.DI container. That, unfortunately, won't work either, because that type isn't registered in MS.DI at all: it is the built-in IPageModelActivatorProvider that takes over.

We will have to figure out how to work with this scenario, without having to revert to weird hacks that prevent DI-container verification. A huge problem here is that the class in question is internal. This makes it very hard to register it by hand.

For now, you can work around the issue by doing the following:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        services.AddSimpleInjector(container, options =>
        {
            options.AddAspNetCore()
                .AddControllerActivation()
                .AddViewComponentActivation()
                .AddPageModelActivation()
                .AddTagHelperActivation();
        });

        services.AddSingleton<IPageModelActivatorProvider>(
            new FixedPageModelActivatorProvider(
                container,
                new SimpleInjectorPageModelActivatorProvider(container)));
    }

Here, FixedPageModelActivatorProvider is a custom class that is defined as follows:

public class FixedPageModelActivatorProvider : IPageModelActivatorProvider
{
    private readonly Container container;
    private readonly IPageModelActivatorProvider decoratee;

    public FixedPageModelActivatorProvider(Container container, IPageModelActivatorProvider decoratee)
    {
        this.container = container;
        this.decoratee = decoratee;
    }

    public Func<PageContext, object> CreateActivator(CompiledPageActionDescriptor descriptor)
    {
        try
        {
            return this.decoratee.CreateActivator(descriptor);
        }
        catch (InvalidOperationException)
        {
            var producer = Lifestyle.Transient.CreateProducer(
                serviceType: descriptor.ModelTypeInfo.AsType(),
                implementationType: descriptor.ModelTypeInfo.AsType(),
                container: this.container);

            return _ => producer.GetInstance();
        }
    }

    public Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor descriptor) =>
        this.decoratee.CreateReleaser(descriptor);
}

This should fix your problem for now, until we have a good fix for this problem.

@dotnetjunkie dotnetjunkie added this to the v4.7 milestone Jun 25, 2019

@dotnetjunkie dotnetjunkie changed the title AddPageModelActivation() has error AddPageModelActivation() can't resolve Microsoft-specified types like LogoutModel<IdentityUser> Jul 16, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.