如何在 .NET 核心中使用 .settings 文件?

Posted

技术标签:

【中文标题】如何在 .NET 核心中使用 .settings 文件?【英文标题】:How to use .settings files in .NET core? 【发布时间】:2018-02-19 00:34:12 【问题描述】:

我正在将应用程序移植到依赖.settings 文件的 .NET 核心。不幸的是,我找不到从 .NET 核心读取它的方法。通常,将以下行添加到 .csproj 会生成一个 TestSettings 类,它可以让我读取设置。

<ItemGroup>
    <None Include="TestSettings.settings">
        <Generator>SettingsSingleFileGenerator</Generator>
    </None>
</ItemGroup>

不幸的是,这似乎不再起作用。我什至无法验证SettingsSingleFileGenerator 是否运行。这个GitHub issue 表明这是新的.csproj 格式的错误,但没有人提供替代方案。

在 .NET core 中读取.settings 文件的正确方法是什么?

【问题讨论】:

即使 SettingsSingleFileGenerator 的特定于 Visual Studio 的问题得到解决,我认为“设置”系统无论如何都不会与 .NET Core 一起使用,因为它们依赖于 System.Configuration。 (我还没有验证是这种情况) 你能写一个快速的 XML 解析器来读取 .settings 文件和运行时 app.config 文件来提取值吗?这可能比尝试让 VS 中的“设置”功能发挥作用更快、更容易。 @Dai - 我绝对可以编写一个 XML 解析器,或者使用现成的解析器来做到这一点,但由于它是一种标准格式,我想使用支持的库来阅读它。我真的不想涉足为 Microsoft 维护设置文件解析器的业务。 此外,从 .NET Core 2.0(我正在使用)开始,System.Configuration 可用。 【参考方案1】:

对于 .NET Core 2.x,请使用 Microsoft.Extensions.Configuration 命名空间(请参阅下面的注释),并且 NuGet 上有大量扩展,您需要从环境变量到 Azure Key Vault(但更现实的是,JSON 文件、XML 等)。

这是一个来自控制台程序的示例,该程序检索设置的方式与我们在为我们的 Azure 站点启动 Kestrel 时使用它们的方式相同:

public static IConfiguration Configuration  get;  = new ConfigurationBuilder()
    .SetBasePath(Directory.GetCurrentDirectory())
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)

    // This allows us to set a system environment variable to Development
    // when running a compiled Release build on a local workstation, so we don't
    // have to alter our real production appsettings file for compiled-local-test.
    //.AddJsonFile($"appsettings.Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production".json", optional: true)

    .AddEnvironmentVariables()
    //.AddAzureKeyVault()
    .Build();

然后在需要设置的代码中,您只需引用 Configuration 或注册 IConfiguration 以进行依赖注入或其他任何操作。

注意:IConfiguration 是只读的,并且可能永远不会根据this 评论获得持久性。因此,如果需要阅读和写作,您将需要一个不同的选项。可能是System.Configuration sans 设计师。

【讨论】:

.NET Core 2.0 中Microsoft.Extensions.ConfigurationSystem.Configuration.ConfigurationManager 有什么区别? 完全重写/重新设计。 System 版本是一个整体的、静态的、以 XML 为中心的特性。新版本可扩展且对 DI 友好(如果您愿意,这也使单元测试变得更容易)。 我还应该注意,如果您使用的是 ASP.NET Core(不仅仅是 .NET Core,或 .NET Standard 类库),您应该使用不同的概念 read about。哦,新系统支持配置绑定,将配置部分反序列化为具有匹配属性的类,非常方便。 我实际上认为比使用 Directory.GetCurrentDirectory() 成为错误的潜在原因(通常是新手错误) 这个答案只对了一半。如果您来这里是为了寻找一个存储来读取和保存数据,不要走 Microsoft.Extensions.Configuration 这条路,它目前是不可变的 (github.com/aspnet/Configuration/issues/385)。似乎 System.Configuration.ConfigurationManager 或自定义 Json.Net impl 是要走的路。呃。【参考方案2】:

正如我在问题中所问的那样,这不可能是“正确的”,但我将其用作权宜之计,直到出现更合理的情况。我不能保证它适用于其他任何人。

将您的 .settings 文件作为嵌入资源包含在内,然后像这样使用它:

private static readonly ConfigurationShim Configuration = new ConfigurationShim("MyApp.Settings.settings");
public static bool MyBoolSetting => (bool) Configuration["MyBoolSetting"];

代码:

internal class ConfigurationShim

    private static readonly XNamespace ns = "http://schemas.microsoft.com/VisualStudio/2004/01/settings";

    private readonly Lazy<IDictionary<string, object>> configuration;

    public ConfigurationShim(string settingsResourceName)
    
        configuration = new Lazy<IDictionary<string, object>>(
            () =>
            
                Assembly assembly = Assembly.GetExecutingAssembly();
                using (Stream stream = assembly.GetManifestResourceStream(settingsResourceName))
                using (var reader = new StreamReader(stream))
                
                    XDocument document = XDocument.Load(reader);
                    return document.Element(ns + "SettingsFile")
                                   .Element(ns + "Settings")
                                   .Elements(ns + "Setting")
                                   .Select(ParseSetting)
                                   .ToDictionary(kv => kv.Item1, kv => kv.Item2);
                
            );
    

    public object this[string property] => configuration.Value[property];

    private static (string, object) ParseSetting(XElement setting)
    
        string name = setting.Attribute("Name").Value;
        string typeName = setting.Attribute("Type").Value;
        string value = setting.Element(ns + "Value").Value;

        Type type = Type.GetType(typeName);
        IEnumerable<ConstructorInfo> ctors = GetSuitableConstructors(type);
        IEnumerable<MethodInfo> staticMethods = GetSuitableStaticMethods(type);

        object obj = null;
        foreach (MethodBase method in ctors.Cast<MethodBase>().Concat(staticMethods))
        
            try
            
                obj = method.Invoke(null, new object[] value);
                break;
            
            catch (TargetInvocationException)
            
                // ignore and try next alternative
            
        

        return (name, obj);
    

    private static IEnumerable<MethodInfo> GetSuitableStaticMethods(Type type)
    
        // To use a static method to construct a type, it must provide a method that
        // returns a subtype of itself and that method must take a single string as
        // an argument. It cannot be generic.
        return type.GetMethods().Where(method =>
        
            ParameterInfo[] parameters = method.GetParameters();
            return !method.ContainsGenericParameters &&
                   method.IsStatic &&
                   parameters.Length == 1 &&
                   parameters[0].ParameterType.IsAssignableFrom(typeof(string)) &&
                   type.IsAssignableFrom(method.ReturnType);
        );
    

    private static IEnumerable<ConstructorInfo> GetSuitableConstructors(Type type)
    
        // We need a constructor of a single string parameter with no generics.
        return type.GetConstructors().Where(ctor =>
        
            ParameterInfo[] parameters = ctor.GetParameters();
            return !ctor.ContainsGenericParameters &&
                   parameters.Length == 1 &&
                   parameters[0].ParameterType.IsAssignableFrom(typeof(string));
        );
    

【讨论】:

,某人,找到一个替代方案。我犹豫着把它贴在这里;有人可能会使用它。 没有太多理由在资源中嵌入设置文件,除非您仅在未找到设置文件时使用它来写入默认文件。首先拥有一个设置文件的想法是让用户能够通过编辑它来配置东西,这在嵌入时并不容易。【参考方案3】:

移植现有项目时,我通常将生成的 Settings.Designer.cs 从旧项目复制到新项目。但我知道,这不利于更改设置文件或添加新的设置键。 我还注意到安装新版本后用户的设置被删除了,.net-Framework-Settings 不是这样。

【讨论】:

看到net Core-Apps可以有几个配置Provider:https://msdn.microsoft.com/en-us/magazine/mt632279.aspx

以上是关于如何在 .NET 核心中使用 .settings 文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ASP.Net 核心代码优先方法中上传图像并使用 post man 发送图像

在 asp.net 核心中使用 NVarChar

ASP.NET MVC 核心外键

在 asp.net 核心中使用输入标签助手 - “值”为空

带有身份的asp.net核心中的依赖注入错误

如何在 .net 和 .net 核心中使用 HttpClient 调用多个客户端 API