How to use .settings files in .NET core?

一个人想着一个人 提交于 2019-12-14 03:44:46

问题


I'm porting an application to .NET core which relies on a .settings file. Unfortunately, I can't find a way to read it from .NET core. Normally, adding the following lines to the .csproj would generate a TestSettings class that would let me read the settings.

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

Unfortunately, this no longer seems to do anything. I can't even verify that the SettingsSingleFileGenerator runs at all. This GitHub issue suggests that this is a bug with the new .csproj format, but no one has offered an alternative.

What is the proper way of reading .settings files in .NET core?


回答1:


For .NET Core 2.x, use the Microsoft.Extensions.Configuration namespace (see note below), and there are tons of extensions on NuGet you'll want to grab for reading from sources ranging from environment variables to Azure Key Vault (but more realistically, JSON files, XML, etc).

Here's an example from a console program that retrieves settings the same way we use them when Kestrel starts up for our Azure sites:

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();

Then in your code that needs settings, you just reference Configuration or you register IConfiguration for dependency injection or whatever.

Note: IConfiguration is read-only and will likely never get persistence per this comment. So if reading AND writing are required, you'll need a different option. Probably System.Configuration sans designer.




回答2:


When porting existing projects I usually copy the generated Settings.Designer.cs from the old to the new project. But I know, this is bad for changing the Settings-file or adding new Settings-Keys. I also noticed that the user's settings were deleted after installing a new version, what was not the case with .net-Framework-Settings.




回答3:


There's no way this is "proper", as I asked in the question, but I'm using this as a stop-gap until something more reasonable comes along. I cannot guarantee it will work for anyone else.

Include your .settings file as an embedded resource, then use it like this:

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

Code:

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));
        });
    }
}


来源:https://stackoverflow.com/questions/48858172/how-to-use-settings-files-in-net-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!