C# 的配置 Class 正在多个环境中使用的库

Configuration for C# Class Library being used in multiple environments

我正在 C# .NET Framework 4.6 中构建一个 class 库,该库用于整合和简化对各种后端数据源的数据访问。这些数据源是 Azure 表、Azure Blob、Redis 缓存、DocumentDB 数据和外部 REST API。所有这些数据都被不同平台上的多个系统使用。在这一点上,我们有一大堆不同的系统,每次后端数据源之一发生微小变化时,都需要单独维护。更改 table 意味着我们需要更新 5 到 7 个代码库中的代码。

问题是这个 class 库将用于 ASP.NET 2 & 4、ASP.NET Core 1.0 和 Windows Forms 应用程序。有可能它也可以用于其他环境。

挑战在于每个位置都需要一种方法来设置自己的配置字符串和访问密钥。我希望这只是您添加信息的配置文件的一部分。图书馆会读取密钥并准备就绪,就像许多图书馆所做的那样。

我的问题是在这些平台上似乎没有访问设置的通用方法。我不想为每个可以使用的平台创建自定义设置访问器。

谁能给我一个易于实施和维护的直接解决方案。理想情况下,它不需要太多外部依赖。

作为附带问题,在处理这些不同的系统时,我是否应该遇到任何其他陷阱。

我不太确定你想要什么:

1) 库配置数据需要位于主项目的配置文件中吗? 2) 库需要将所有其他项目的配置数据嵌入到它自己的配置中吗?

如果#1 如果您需要什么,那么它就像使用您的配置文件一样简单。我目前在生产中有 4 个使用 4 个库的 Web 应用程序,其中 2 个在使用它的项目上有自定义配置。我使用 appSettings webconfig 部分。图书馆将使用以下方式读取该配置文件:

ConfigurationManager.AppSettings["<CONFIG KEY HERE>"]

如果#2,则大致相同。只需为包含所有内容的库编写自定义配置,然后将其与所有数据一起发布。但我更喜欢做 1 号。

如果我只是错了你的问题,那么抱歉!如果你更详细地解释你的场景是什么样的,那么我很乐意帮助你。

As a side question, are there any other gotchas I should expect dealing with these different systems.

这很糟糕很糟糕!我只是有一个相关的问题。检查我的post:Is Windows Universal App for me? - UI Migration

拥有框架 2 和 4 会给您带来问题,还会混合使用平台和特别是最新的微软技术,例如 aspnet core 和 UWA。

我会检查 TWICE 并做一些广泛的测试,然后再花时间编写一些根本无法工作的东西。

干杯

如果只是 class 需要连接字符串,请将连接字符串放在 class 的构造函数中。 class 不应直接从 web.config 或其他任何地方获取字符串。

像这样:

public class ReadsSomethingFromSql
{
    private readonly string _connectionString;

    public ReadsSomethingFromSql(string connectionString)
    {
        _connectionString = connectionString;
    }

    public void InsertSomethingIntoSql()
    {
        using (var connection = new SqlConnection(_connectionString))
        {
            //Do something with connection string
        }
    }
}

这样 class 不负责从 web.config 读取数据。这可能真的很令人困惑,因为使用 class 的人现在可能知道 class 期望连接字符串位于 web.config 中。通过将它放在构造函数中,很明显 class 需要一个连接字符串。

如果您不使用依赖注入,那么您的调用方法可以这样做:

var reader = new ReadsSomethingFromSql(
    ConfigurationManager.ConnectionStrings["myConnection"].ConnectionString);

但这只能解决问题。如果您在自己的库 中创建 class 实例 那么您仍然会遇到同样的问题 - 您的库直接从 web.config 读取。

更好的解决方案是使用依赖注入来指定您的 class 所依赖的所有值。 (在这个 post 中扩展是一个很大的主题。这里是 starting example 使用 Castle Windsor 的依赖注入。)

使用它,您可以像这样指定连接字符串的来源:

container.Register(
    Component.For<ReadsSomethingFromSql>()
        .DependsOn(Dependency.OnValue("connectionString",
            ConfigurationManager.ConnectionStrings["myConnection"].ConnectionString))
    );

如果它不仅仅是一个连接字符串,可能还有一些您的 class 需要的设置,那么您可以创建一个接口,如下所示:

public interface ISettings
{
    string Setting1 { get; }
    int SomeOtherSetting { get; }
}

并将其放入 class 的构造函数中,就像上面的连接字符串一样。

public class MyClass
{
    private readonly ISettings settings;

    public MyClass(ISettings settings)
    {
        _settings = settings;
    }

那样的话,您的 class 指的是 _settings,它是 ISettings 的一个实例,但它 "know" 并没有说明实现是什么。

然后您可以创建一个 class 实现 ISettings 从配置中获取值,如下所示:

public class Settings : ISettings
{
    public string Setting1 { get { return ConfigurationManager.AppSettings["MyClass:Setting1"]; } }
    public int SomeOtherSetting {
        get
        {
            return Int32.Parse(ConfigurationManager.AppSettings["MyClass:SomeOtherSettings"]);
        }
    } }
}

这将解决您的 class 问题,具体取决于配置。它不依赖于配置。这将取决于 ISettings.

使用 Windsor,您的应用程序将像这样配置它:

container.Register(Component.For<ISettings,Settings>());

我并没有真正用这些粗略的例子来做依赖注入正义。但我带着完全相同的问题开始了完全相同的道路。依赖注入就是答案,并且还会在许多其他方面使您受益。

更新: 您提到您不想修改 class 的使用者。我想我看到了部分问题 - 您的 Core 1.0 应用程序不会有 web.config.

你可以妥协。修改 class 的构造函数并使参数可选。这样,当您编写新的消费者时,您可以使用依赖注入。老消费者可以像现在这样工作,像这样:

public class ReadsSomethingFromSql
{
    private readonly string _connectionString;

    public ReadsSomethingFromSql(string connectionString = null)
    {
        _connectionString = connectionString
            ?? ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
    }