【问题标题】:Using IConfiguration in C# Class Library在 C# 类库中使用 IConfiguration
【发布时间】:2015-01-10 19:40:48
【问题描述】:

我正在使用 C# 和 Core .NET 构建一个类库。我正在尝试使用来自config.json 文件的配置。以下是该文件的内容:

config.json

{
  "emailAddress":"someone@somewhere.com"
}

为了尝试将config.json 用于我的配置,我在project.json 文件中引用了Microsoft.Framework.ConfigurationModel.Json。在我的代码中,我有以下内容:

MyClass.cs

using Microsoft.Framework.ConfigurationModel;
public class MyClass
{
  public string GetEmailAddress()
  {
//    return ConfigurationManager.AppSettings["emailAddress"];  This is the approach I had been using since .NET 2.0
    return ?;  // What goes here?
  }
}

从 .NET 2.0 开始,我一直在使用 ConfigurationManager.AppSettings["emailAddress"]。但是,我现在正在尝试通过IConfiguration 学习如何以新的方式做到这一点。我的问题是,这是一个类库。出于这个原因,我不确定配置文件是如何、在何处或何时加载的。在传统的 .NET 中,我只需要为 ASP.NET 项目命名一个文件 web.config,为其他项目命名一个 app.config。现在,我不确定。我有一个 ASP.NET MVC 6 项目和一个 XUnit 项目。所以,我试图弄清楚如何在这两种情况下使用config.json

谢谢!

【问题讨论】:

    标签: c# asp.net-core .net-core


    【解决方案1】:

    IMO 类库应该与应用程序设置数据无关。一般来说,图书馆消费者是关心这些细节的人。是的,这并不总是正确的(例如,如果您有一个进行 RSA 加密/解密的类,您可能需要一些私有配置以允许私钥生成/存储),但在大多数情况下,这是正确的。

    因此,一般来说,尽量将应用程序设置排除在类库之外,并让消费者提供此类数据。在您的评论中,您提到了数据库的连接字符串。这是将数据保留在类库之外的完美示例。图书馆不应该关心它调用什么数据库来读取,只是它需要从一个数据库中读取。下面的例子(如果有一些错误,我很抱歉,因为我是从记忆中写出来的):

    图书馆

    使用连接字符串的库类

    public class LibraryClassThatNeedsConnectionString
    {
        private string connectionString;
    
        public LibraryClassThatNeedsConnectionString(string connectionString)
        {
            this.connectionString = connectionString;
        }
    
        public string ReadTheDatabase(int somePrimaryKeyIdToRead)
        {
            var result = string.Empty;
    
            // Read your database and set result
    
            return result;
        }
    }
    

    应用程序

    appsettings.json

    {
      "DatabaseSettings": {
        "ConnectionString": "MySuperCoolConnectionStringWouldGoHere"
      }
    }
    

    数据库设置.cs

    public class DatabaseSettings
    {
        public string ConnectionString { get; set; }
    }
    

    Startup.cs

    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            Configuration = new ConfigurationBuilder()
                            .SetBasePath(env.ContentRootPath)
                            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                            .AddEnvironmentVariables()
                            .Build();
        }
    
        public IConfigurationRoot Configuration { get; }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            // Setup logging
            // Configure app
    
        }
    
        public void ConfigureServices(IServiceCollection services)
        {
            // Configure services
            services.Configure<DatabaseSettings>(Configuration.GetSection("DatabaseSettings"));
            services.AddOptions();
    
            // Register our class that reads the DB into the DI framework
            services.AddTransient<IInterfaceForClass, ClassThatNeedsToReadDatabaseUsingLibrary>();
        }
    }
    

    使用库类读取数据库的类

    public interface IInterfaceForClass
    {
        string ReadDatabaseUsingClassLibrary(int somePrimaryKeyIdToRead);
    }
    
    public class ClassThatNeedsToReadDatabaseUsingLibrary : IInterfaceForClass
    {
        private DatabaseSettings dbSettings;
        private LibraryClassThatNeedsConnectionString libraryClassThatNeedsConnectionString;
    
        public ClassThatNeedsToReadDatabaseUsingLibrary(IOptions<DatabaseSettings> dbOptions)
        {
            this.dbSettings = dbOptions.Value;
            this.libraryClassThatNeedsConnectionString = new LibraryClassThatNeedsConnectionString(this.dbSettings.ConnectionString);
        }
    
        public string ReadDatabaseUsingClassLibrary(int somePrimaryKeyIdToRead)
        {
            return this.libraryClassThatNeedsConnectionString.ReadTheDatabase(somePrimaryKeyIdToRead);
        }
    }
    

    一些处理从数据库读取的 UI 内容的控制器类

    public class SomeController : Controller
    {
        private readonly classThatReadsFromDb;
    
        public SomeController(IInterfaceForClass classThatReadsFromDb)
        {
            this.classThatReadsFromDb = classThatReadsFromDb;
        }
    
        // Controller methods
    }
    

    TL;DR

    尽量避免在类库中使用应用程序设置。相反,让您的类库对此类设置不可知,并让消费者传递这些设置。

    编辑:

    我将依赖注入添加到控制器类中,以演示使用依赖注入来构建从数据库读取的类。这让 DI 系统可以解决必要的依赖关系(例如 DB 选项)。

    这是一种方法(也是最好的方法)。另一种方法是将 IOptions 注入控制器并手动更新从数据库读取的类并将选项传入(不是最佳实践,DI 是更好的方法)

    【讨论】:

    • 这对我设置一个新项目很有帮助。 :)
    • 我不反对类库中的 DI 方法。绝对是要走的路。但就访问层而言,我的信念是静态的,将依赖项注入方法级别。不是构造函数。我意识到这是一个古老的论点,没有正确的答案,但我倾向于转向静态的一面。
    • @PimBrouwers 如果您指的是在构造函数级别注入连接字符串,那么这不是个人偏好。这就是 .NET Core 现在处理将配置设置导入您的应用程序的方式。他们推荐您在这里看到的选项模式,而不是通过静态配置类访问它们的旧方式。
    • @McGaz 这甚至与我的回答所包含的内容相去甚远。再看看我的代码。 1) 这里唯一的“额外”代码是将设置添加到 appsettings.json,创建一个类以匹配这些设置,并使用选项模式注入设置(.NET Core 1.0 中的标准做法)。 2) 库在初始化时只需要一次连接字符串(这是您无法避免的。您将始终需要知道从哪里读取数据)。说真的,再看一遍代码,付诸实践。这是非常基本的东西,也是正确的做法。
    • 恕我直言,这应该是选择的答案。它不仅得到了很好的解释和架构,而且还遵循了 .NET Core 提出的最佳实践。 @StephenPorter 非常感谢。我还没有看到有关如何为外部库配置 IOptions 的示例。现在我已经实现了它似乎很明显,但是我必须阅读你的答案才能得到一分钱:)
    【解决方案2】:

    这应该可行。需要安装包Microsoft.Extensions.Configuration.Json

     public static class Config
      {
        private static IConfiguration configuration;
        static Config()
        {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
            configuration = builder.Build();
        }
    
        public static string Get(string name)
        {
            string appSettings = configuration[name];
            return appSettings;
        }
    }
    

    【讨论】:

      【解决方案3】:

      从没用过,但是快速搜索一下就找到了这个……

      var configuration = new Configuration();
      configuration.AddJsonFile("config.json");
      var emailAddress = configuration.Get("emailAddress");
      

      也许你可以试试。

      【讨论】:

      • 我也看到了。但是,在需要配置信息的每个类中加载 JSON 文件似乎效率很低。例如,如果有人需要一个数据库连接字符串怎么办。必须有某种方式来加载配置信息,以便在整个应用程序范围内都可用。
      • @user70192 查看类似问题的答案:stackoverflow.com/questions/28232364/… 它展示了如何在应用启动时仅一次获取数据,然后使用依赖注入来允许任何应用中的其他组件来获取相同的数据。
      • @Eilon 我的 .net 核心库类没有 Startup 类...
      • @LetieTechera 类库不会有 Startup 类。只有“应用程序”项目才会有这些 - 例如 ASP.NET Core Web 应用程序。但是你仍然可以在任何库的任何类的任何代码中包含一些类似的代码(只是不是 Startup 类本身)。
      • @Eilon 感谢您的信息! .net 核心库与 .net 框架应用程序不太兼容,我最终将我的应用程序迁移到与 win 表单兼容的 .net core 3.0。并且引用我的 .net core 2.2 库效果很好
      【解决方案4】:

      首先在您的.csproj 文件中添加target that hocks in the build process,如果以下内容不符合您的需求,请参阅链接以获取更多选项,例如发布

      <Target Name="AddConfig" AfterTargets="AfterBuild">
          <Copy SourceFiles="config.json" DestinationFolder="$(OutDir)" />
      </Target>
      

      你可以像下面这样使用它

      using Microsoft.Framework.ConfigurationModel;
      using Microsoft.Extensions.Configuration;
      using System;
      
      public class MyClass {
          public string GetEmailAddress() {
              //For example purpose only, try to move this to a right place like configuration manager class
              string basePath= System.AppContext.BaseDirectory;
              IConfigurationRoot configuration= new ConfigurationBuilder()
                  .SetBasePath(basePath)
                  .AddJsonFile("config.json")
                  .Build();
      
              return configuration.Get("emailAddress");
          }
      }
      

      【讨论】:

        【解决方案5】:

        如何使用 IConfiguration 将 AppSettings.Json 键值读入 C# 控制器。

        如果有人想看,以 Asp.net Core .Net 5.0 为例。我已经完成了上述答案,并为我的应用程序稍微调整了我的代码。

        如果您想了解如何在控制台应用程序中使用它,请访问我在此 link 上的回答,我还添加了带有电子邮件地址的示例。


        我的 AppSettings.Json 是:

        {
        "AppSettings": {
            "FTPLocation": "\\\\hostname\\\\c$\\\\FTPMainFolder\\\\ftpFolder\\\\Test\\",
            "FTPUri": "ftp://hostname.domainname.com/foldername/",
            "CSVFileName": "Test Load Planning.csv"  
                        },
        "ConnectionStrings": 
         {
         "AppDbConnString": "Server=sqlserverhostname.domainname.com;Database=DBName;Trusted_Connection=True; MultipleActiveResultSets=true"   },
         "ADSecurityGroups": { "UserSecurityGroups": "AD-DL-GROUP-NAME;AD-DL-GROUP2-NAME"},
         "Logging": 
          {
            "LogLevel": {
                "Default": "Warning"    
               }  
           }
        }
        

        我的 LoginController.cs 是:

        using Microsoft.Extensions.Configuration;
        public class LoginController : BaseController
        {
            
            private readonly ILoginDataServices _loginDataServices;
            private readonly IConfiguration _configuration;
            public IActionResult Index()
            {
                return View();
            }
        
        
            public LoginController(ILoginDataServices loginDataServices, IConfiguration configuration)
            {
               
                    _loginDataServices = loginDataServices;
                    _configuration = configuration;
                
            }
        
        
            public bool CheckLogin(string userName, string password)
            {
                if (CheckIfValidEmployee(userName))
                {
                    //////checking code here....
                }
                else
                {
                    return false;
                }
            }
        
            bool CheckIfValidEmployee(string userName)
            {
        
                var securityGroups = _configuration.GetSection("ADSecurityGroups:UserSecurityGroups").Value.Split(';');
                 Console.WriteLine(securityGroups);
               ////////Code to check user exists into security group or not using variable value
             }
        

        【讨论】:

          【解决方案6】:

          您也可以通过右键单击.csproject -> properties-> settings-> 在右侧窗口中添加新属性来设置类库的属性。 确保在访问修饰符下拉列表中选择访问修饰符为公开。

          现在,将类库项目引用添加到您的 .net 核心项目。

          如下所述创建 appSettings.cs 类

          public class AppSettings
          {
              public string MyConnectionString { get; set; }
          }
          

          设置键值appSettings.json

          "AppSettings": {
          "MyConnectionString": "yourconnectionstring",
          

          },

          现在,我们只需要从 appSettings.json 和 在 Startup.cs 中将属性设置为类库,如下所示。

          // This method gets called by the runtime. Use this method to add services to the container
              public void ConfigureServices(IServiceCollection services)
              {
                  services.AddControllers();
                  // inject App setting
                  var appSettingSection = Configuration.GetSection("AppSettings");
                  services.Configure<AppSettings>(appSettingSection);
                  var appsetting = appSettingSection.Get<AppSettings>();
                  // set connection string in .csproject properties.
                  classLibraryProject.Properties.Settings.Default.Properties["MyConnectionString"].DefaultValue = appsetting.MyconnectionString;
          
          
              }
          

          注意:

          • 确保 MyConnectionString 键。三个文件中的内容应该相同。
          • 确保在 ClassLibrary 项目中将访问修饰符设置为 Public。

          我希望这会有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-04-27
            • 2022-01-15
            • 2010-09-08
            • 1970-01-01
            • 2022-10-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多