IPostConfigureOptions<ApplicationPartManager> not triggered after application build

Viewed 99

I try to implement Generic Controller. So for that I try to use;

IPostConfigureOptions<ApplicationPartManager>
IApplicationFeatureProvider<ControllerFeature>

I use PostConfigureOptions because of I want to discover after built. I cannot use

services.AddMvc().AddRazorRuntimeCompilation().ConfigureApplicationPartManager(m => m.FeatureProviders.Add(new GenericControllerFeatureProvider()));

because it is in Startup, however I register my controller inside my module service builder.

My dependency injection code:

//working 
Services.ConfigureOptions(typeof(GenericPageConfigureOptions<,>).MakeGenericType(builder.UserType, t));
//not working  
Services.ConfigureOptions(typeof(GenericControllerConfigureOptions<,>).MakeGenericType(builder.UserType, t));

// My IPostOptions implementations
// This is working as expected.
public class GenericPageConfigureOptions<TEntity, TKey> : IPostConfigureOptions<RazorPagesOptions> where TEntity : class where TKey : IEquatable<TKey> 
{
    public void PostConfigure(string name, RazorPagesOptions options)
    {
        name = name ?? throw new ArgumentNullException(nameof(name));
        options = options ?? throw new ArgumentNullException(nameof(options));

        var convention = new GenericPageModelConvention<TEntity, TKey>();
        options.Conventions.AddAreaFolderApplicationModelConvention("Identity", "/", pam => convention.Apply(pam));
    }
}

//not working
public class GenericControllerConfigureOptions<TEntity, TKey> : IPostConfigureOptions<ApplicationPartManager> where TEntity : class where TKey : IEquatable<TKey> 
{
    public void PostConfigure(string name, ApplicationPartManager options) 
    {
        name = name ?? throw new ArgumentNullException(nameof(name));
        options = options ?? throw new ArgumentNullException(nameof(options));

        var provider = new GenericControllerFeatureProvider<TEntity, TKey>();
        options.FeatureProviders.Add(provider);
    }
}

Finally my feature provider class is looking like that:

public class GenericControllerFeatureProvider<TEntity, TKey> : IApplicationFeatureProvider<ControllerFeature> where TEntity : class where TKey : IEquatable<TKey> 
{
    public void PopulateFeature(IEnumerable<ApplicationPart> parts, ControllerFeature feature) 
    {
        var candidates = AppDomain.CurrentDomain.GetAssemblies().SelectMany(s => s.GetTypes()).Where(x => x.GetCustomAttributes<GeneratedControllerAttribute>().Any());

        foreach (var candidate in candidates) {
            var typeName = candidate.Name.Remove(candidate.Name.IndexOf("Controller"));
            if (!feature.Controllers.Any(t => t.Name == typeName)) {
                var defaultUIAttribute = candidate.GetTypeInfo().GetCustomAttribute<GeneratedControllerAttribute>();

                var controllerType = defaultUIAttribute.Template.MakeGenericType(typeof(TEntity), typeof(TKey)).GetTypeInfo();
                feature.Controllers.Add(controllerType);
            }
        }
    }
}

An example controller:

[GeneratedController(typeof(IdentityController<,>))]
public class IdentityController<TUser, TKey> : Controller
{ 
    private IUserManager<TUser, TKey> _userManager;

    public IdentityController(IUserManager<TUser, TKey> userManager) 
    {
        _userManager = userManager;
    }
}
1 Answers

IPostConfigurationOptions is triggered only if you consume the options.

You can try to access your option ApplicationPartManager using controller injection:

[GeneratedController(typeof(IdentityController<,>))]
public class IdentityController<TUser, TKey> : Controller
{ 
    private IUserManager<TUser, TKey> _userManager;

    public IdentityController(IOptions<ApplicationPartManager> option, IUserManager<TUser, TKey> userManager) 
    {
        _userManager = userManager;
    }
}
Related