locked
Make this code generic RRS feed

  • Question

  • User1634732699 posted

    Hi (again)


    I am trying to get used for generics. Playing with ASPNet Core 3.0

    In my scenario I have configured a startup filter for configuration validation. Specifically I want to validate IdentitySettings that are holding in appsettings. Well.
    I expose the working code and I would like to know a solution to make it generic and reusable.

    In configure method I am doing 

      builder.ConfigureServices((services) =>
                {
                  
                    services.Configure<IdentityStartupSettings>(Configuration, binder => new IdentityStartupSettings());
    
                    services.AddSingleton(resolver =>
                    {
                        var identityStartupConfig = resolver.GetRequiredService<IOptions<IdentityStartupSettings>>().Value;
                        return identityStartupConfig;
                    });
                    services.AddConfigValidation<IdentityStartupSettings>();

    IdentityStartupSettings is my POCO class that has settings definition. I am using DataAnnotations.

    AddConfigValidation is an extension method I did to perform validation service injection

     public static IServiceCollection AddConfigValidation<T>(this IServiceCollection services) 
                where T : IValidateConfig
            {
               
                services.AddTransient<IStartupFilter, ValidateConfigFilter>();
              
    
                services.AddSingleton<IValidateConfig>(provider => provider.GetRequiredService<T>());
    
                return services;
            }

    Now the questions:
    First is simple. When I am doing services.Configure<IdentityStartupSettings>(Configuration, binder => new IdentityStartupSettings()); I am not sure if binder => new IdentityStartupSettings() means that it with bind to an instance of IdentityStartupSettings. I would like to know a scenario where it parameter is more interesting that here. I am curious.

    Second. 
    I would like to transform the code for being fully generic. Where I can overload AddConfigValidation<T>(this IServiceCollection services) to accept a second parameter, the instance of IConfiguration (or IConfigurationSection in my case) then being:  AddConfigValidation<T>(this IServiceCollection services, IConfiguration config)

    And perform Configuration in the same method:
    The part where I am doing:

      services.Configure<IdentityStartupSettings>(Configuration, binder => new IdentityStartupSettings());
    
                    services.AddSingleton(resolver =>
                    {
                        var identityStartupConfig = resolver.GetRequiredService<IOptions<IdentityStartupSettings>>().Value;
                        return identityStartupConfig;
                    });

    being inside, something like this (what is not working):

    public static IServiceCollection AddConfigValidation<T>(this IServiceCollection services, IConfiguration config) 
                where T : IValidateConfig
            {
               
    services.Configure<T>(config);
    
                    services.AddSingleton(resolver =>
                    {
                        var configValue = resolver.GetRequiredService<IOptions<T>>().Value;
                        return configValue;
                    });
    
                services.AddTransient<IStartupFilter, ValidateConfigFilter>();
              
    
                services.AddSingleton<IValidateConfig>(provider => provider.GetRequiredService<T>());
    
                return services;
            }

     First error, the line services.Configure<T>(config); is asking for an Action instead of an IConfiguration instance. What is not happening when I do it outside.
    Maybe you can give me some guidance.

    So pleased, thanks in advance.



    Tuesday, July 2, 2019 8:55 PM

Answers

  • User-1764593085 posted

    Hi SamML,

    Try to use below code which lets T inherits class,IValidConfig, new(), see Constraints on type parameters

    public static class Extensions
        {
            public static IServiceCollection AddConfigValidation<T>(this IServiceCollection services, IConfiguration config)
                 where T : class, IValidateConfig, new()
            {
                services.Configure<T>(config);
    
                services.AddSingleton(resolver =>
                {
                    var configValue = resolver.GetRequiredService<IOptions<T>>().Value;
                    return configValue;
                });
                services.AddTransient<IStartupFilter, ValidateConfigFilter>();
    
                services.AddSingleton<IValidateConfig>(provider => provider.GetRequiredService<T>());
    
                return services;
            }
        }

    For more details, refer to below tutorail (its github):

    https://medium.com/cheranga/validating-configurations-in-an-asp-net-core-2-1-application-using-data-annotations-71e852dd9c2c

    Best Regards,

    Xing

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 3, 2019 9:25 AM

All replies

  • User475983607 posted

    In my scenario I have configured a startup filter for configuration validation. Specifically I want to validate IdentitySettings that are holding in appsettings

    I don't get it. 

    Why are you verifying startup setting that you must write?  What problem does this solve and if you already know what the validate values are then why not just set them in the first place?

    Tuesday, July 2, 2019 9:07 PM
  • User1634732699 posted

    Thanks for answer-

    Because I am trying to do it reusable. Then I can define a class/POCO object with required values and use it to load settings of external assemblies. POCO and Startup are living in same assembly I mean. But it is used by another client.

    For example in this case I load Identity services from an external assembly and I want If any other scenario use it, and it/me/he does forget to configure required values for this assembly error tells me "Man! You have forget some required configuration".

    Imagine I want to make different clients, I always will know why it is failing when I see a custom exception related to configuration values If I forget to define those in my appsettings.json.

    In this case is all error management related to.

    Bytheway, purpose is not that important since I try to understand why I cant "transform" it to not being binded to a specific class like IdentityStartupSettings in my case.

    Thanks.

    Wednesday, July 3, 2019 6:25 AM
  • User-1764593085 posted

    Hi SamML,

    Try to use below code which lets T inherits class,IValidConfig, new(), see Constraints on type parameters

    public static class Extensions
        {
            public static IServiceCollection AddConfigValidation<T>(this IServiceCollection services, IConfiguration config)
                 where T : class, IValidateConfig, new()
            {
                services.Configure<T>(config);
    
                services.AddSingleton(resolver =>
                {
                    var configValue = resolver.GetRequiredService<IOptions<T>>().Value;
                    return configValue;
                });
                services.AddTransient<IStartupFilter, ValidateConfigFilter>();
    
                services.AddSingleton<IValidateConfig>(provider => provider.GetRequiredService<T>());
    
                return services;
            }
        }

    For more details, refer to below tutorail (its github):

    https://medium.com/cheranga/validating-configurations-in-an-asp-net-core-2-1-application-using-data-annotations-71e852dd9c2c

    Best Regards,

    Xing

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 3, 2019 9:25 AM
  • User1634732699 posted

    It worked. 


    I followed that tutorial time ago for 2.1 and 2.2 version and now I was playing with version 3.0 and still was usefull. 

    But I wanted to do a different aproach this time as you can see.

    Thanks for your usefull answer

    Wednesday, July 3, 2019 9:34 AM