IOptions Injection

前端 未结 5 1752
情书的邮戳
情书的邮戳 2020-12-08 19:17

It seems to me that it\'s a bad idea to have a domain service require an instance of IOptions to pass it configuration. Now I\'ve got pull additional (unnecessary?) depend

5条回答
  •  轮回少年
    2020-12-08 20:16

    While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.

    This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:

    public static class ConfigurationHelper
    {
        public static T GetObjectFromConfigSection(
            this IConfigurationRoot configurationRoot,
            string configSection) where T : new()
        {
            var result = new T();
    
            foreach (var propInfo in typeof(T).GetProperties())
            {
                var propertyType = propInfo.PropertyType;
                if (propInfo?.CanWrite ?? false)
                {
                    var value = Convert.ChangeType(configurationRoot.GetValue($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
                    propInfo.SetValue(result, value, null);
                }
            }
    
            return result;
    
        }
    }
    

    There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:

    services.AddSingleton((_) =>
        Configuration.GetObjectFromConfigSection("DataStoreConfiguration"));
    

    The appSettings.json config looked something like the following:

    {
      "DataStoreConfiguration": {
        "ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
        "MeaningOfLifeInt" : "42"
      },
     "AnotherSection" : {
       "Prop1" : "etc."
      }
    }
    

    The DataStoreConfiguration class was defined in my library project and looked like the following:

    namespace MyLibrary.DataAccessors
    {
        public class DataStoreConfiguration
        {
            public string ConnectionString { get; set; }
            public int MeaningOfLifeInt { get; set; }
        }
    }
    

    With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:

    using System.Data.SqlClient;
    
    namespace MyLibrary.DataAccessors
    {
        public class DatabaseConnectionFactory : IDatabaseConnectionFactory
        {
    
            private readonly DataStoreConfiguration dataStoreConfiguration;
    
            public DatabaseConnectionFactory(
                DataStoreConfiguration dataStoreConfiguration)
            {
                // Here we inject a concrete instance of DataStoreConfiguration
                // without the `IOption` wrapper.
                this.dataStoreConfiguration = dataStoreConfiguration;
            }
    
            public SqlConnection NewConnection()
            {
                return new SqlConnection(dataStoreConfiguration.ConnectionString);
            }
        }
    }
    

    Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.

提交回复
热议问题