dotnet / dotnet-api-docs

.NET API reference documentation (.NET 5+, .NET Core, .NET Framework)
https://docs.microsoft.com/dotnet/api/
Other
708 stars 1.55k forks source link

Make Microsoft.Extensions.Configuration.Xml more tolerant against not supported XML features #8257

Open amaic opened 5 years ago

amaic commented 5 years ago

I ran into a problem with the current implementation of Microsoft.Extensions.Configuration.Xml.

With Visual Studio 2019 16.2.1 I created a new Console App (.NET Framework) application, added NuGet packages Microsoft.Extensions.Configuration.Xml and NLog.Extensions.Logging, added code var configuration = new ConfigurationBuilder().AddXmlFile(Assembly.GetExecutingAssembly().Location + ".config").Build(); into main function, started the program and got an exception:

System.FormatException HResult=0x80131537 Message=XML namespaces are not supported. Line 7, position 22. Source=Microsoft.Extensions.Configuration.FileExtensions StackTrace: at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload) at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load() at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers) at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build() at ConsoleApp1.Program.Main(String[] args)

I looked into the generated app.config file and found following XML data:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Extensions.Configuration.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Extensions.Primitives" publicKeyToken="adb9793829ddae60" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.2.0.0" newVersion="2.2.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

The reason of FormatException is attribute "xmlns" of element "assemblyBinding".

I wonder, why files with XML namespace declarations are completely rejected.

My expectation would be, that XML features, that are not yet implemented or cannot be implemented due to their nature of not being mappable to key-value-pairs, are ignored, perhaps writing a warning into log file and all other elements are loaded into memory.

So does it make sense to change the behaviour, instead of throwing an exception ignoring such elements and write a warning into log file?

Andrey-32rus commented 4 years ago

I have the same problem and want to know about solution of this problem

ghost commented 4 years ago

As part of the migration of components from dotnet/extensions to dotnet/runtime (https://github.com/aspnet/Announcements/issues/411) we will be bulk closing some of the older issues. If you are still interested in having this issue addressed, just comment and the issue will be automatically reactivated (even if you aren't the author). When you do that, I'll page the team to come take a look. If you've moved on or workaround the issue and no longer need this change, just ignore this and the issue will be closed in 7 days.

If you know that the issue affects a package that has moved to a different repo, please consider re-opening the issue in that repo. If you're unsure, that's OK, someone from the team can help!

brumlemann commented 3 years ago

What happened to this one? Superseded by another solution without trace or just stale (as hinted too) because of no action?

ghost commented 3 years ago

Paging @dotnet/extensions-migration ! This issue has been revived from staleness. Please take a look and route to the appropriate repository.

ghost commented 3 years ago

Tagging subscribers to this area: @maryamariyan See info in area-owners.md if you want to be subscribed.

Issue Details
I ran into a problem with the current implementation of Microsoft.Extensions.Configuration.Xml. With Visual Studio 2019 16.2.1 I created a new Console App (.NET Framework) application, added NuGet packages Microsoft.Extensions.Configuration.Xml and NLog.Extensions.Logging, added code `var configuration = new ConfigurationBuilder().AddXmlFile(Assembly.GetExecutingAssembly().Location + ".config").Build();` into main function, started the program and got an exception: > System.FormatException > HResult=0x80131537 > Message=XML namespaces are not supported. Line 7, position 22. > Source=Microsoft.Extensions.Configuration.FileExtensions > StackTrace: > at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload) > at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load() > at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers) > at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build() > at ConsoleApp1.Program.Main(String[] args) I looked into the generated app.config file and found following XML data: ``` ``` The reason of FormatException is attribute "xmlns" of element "assemblyBinding". I wonder, why files with XML namespace declarations are completely rejected. My expectation would be, that XML features, that are not yet implemented or cannot be implemented due to their nature of not being mappable to key-value-pairs, are ignored, perhaps writing a warning into log file and all other elements are loaded into memory. So does it make sense to change the behaviour, instead of throwing an exception ignoring such elements and write a warning into log file?
Author: amaic
Assignees: -
Labels: `area-Extensions-Configuration`, `untriaged`
Milestone: -
AleksandrEfimov commented 3 years ago

Yes, it`s the problem. I simply wanted get configuration from old App.config after migration from .net 4.7.1 to Core 3.1. Please save hours of live other programmers - solve this issue.

ericstj commented 3 years ago

CC @maryamariyan @eerhardt

I can imagine folks might have preferences both ways. Some folks might feel confused if we silently dropped members of the XML document, not realizing that it was due to the namespace. That feels like much worse behavior than simply disallowing the namespace.

What's the scenario for actually using Microsoft.Extensions.Configuration.Xml with namespaces? I see above folks are using it to read a ,NETFramework app.config which doesn't seem like a good idea. Those are meant to be backed with strongly typed configuration data types and much fidelity will be lost. The format expected by Microsoft.Extensions.Configuration.Xml is much simpler: https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration-providers#xml-configuration-provider.

Maybe I'm missing something here. What's leading folks to use XML files that have namespaces and other content that's not meant for M.E.C.XML?

Trinitron commented 3 years ago

I do not want to create a new issue for that, but Microsoft.Extensions.Configuration lib disrespects key/value pairs from App.config files: https://docs.microsoft.com/en-us/troubleshoot/dotnet/csharp/store-custom-information-config-file It blocks app inicialization process for AzureFunctions (in my case) with this error: [2021-08-10T08:22:48.508Z] A host error has occurred during startup operation '63d277b2-fefe-4ebd-87ec-d7c2f59051ef'. [2021-08-10T08:22:48.510Z] Microsoft.Extensions.Configuration.Xml: A duplicate key 'appSettings:add:key' was found. Line 5, position 10. This is a general XML configuration file (with this problem) you can take from the link above: `<?xml version="1.0" encoding="utf-8" ?>

` Every key in appSettinngs treat like duplicate without specifying 'name' attribute.
drarleigh commented 2 years ago

It would be nice to make this work. "Using it to read a ,NETFramework app.config" may not sound like a good idea but it sounds like a good transition strategy to me. We have a lot invested in our config files and we would like to leverage that as we introduce newer functionality that works with an instance of IConfiguration.

kgiszewski commented 2 years ago

Can we not have a flag to "ignore namespaced" elements?

perahoky commented 2 years ago

i ran into the same problem. a warning somewhere in the documentation or a "ignoreNotSupportedFeatures" or "ignoreNamespaces" flag or something would be nice. Would safe hours of work to other developers. I just want to read simple elements from the app.config - now i need to tear it down to System.Configuration.ConfigurationManager due to migration ...

Vaccano commented 1 year ago

I too would love an IgnoreNameSpaces flag. I am now having to consider rolling my own configuration provider of the XML file because of xml namespaces in the configuration file.

udlose commented 11 months ago

CC @maryamariyan @eerhardt

I can imagine folks might have preferences both ways. Some folks might feel confused if we silently dropped members of the XML document, not realizing that it was due to the namespace. That feels like much worse behavior than simply disallowing the namespace.

What's the scenario for actually using Microsoft.Extensions.Configuration.Xml with namespaces? I see above folks are using it to read a ,NETFramework app.config which doesn't seem like a good idea. Those are meant to be backed with strongly typed configuration data types and much fidelity will be lost. The format expected by Microsoft.Extensions.Configuration.Xml is much simpler: https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration-providers#xml-configuration-provider.

Maybe I'm missing something here. What's leading folks to use XML files that have namespaces and other content that's not meant for M.E.C.XML?

@ericstj I am creating a .NET Standard 2.0 library that needs to support both .NET Framework (app.config, web.config) and .NET configuration styles (appsettings.json).

If I'm going to use M.E.C to handle this, wouldn't I need to use something like the following?

            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddXmlFile("App.config", optional: true, reloadOnChange: true)
                .AddXmlFile("ConsoleApp1.exe.config", optional: true, reloadOnChange: true)
                .AddXmlFile("web.config", optional: true, reloadOnChange: true)
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

            var config = builder.Build();

The above code causes the exception the OP provides. How would you suggest doing this?

ericstj commented 11 months ago

AddXmlFile isn't meant for use with .NETFramework config files - it might work in some cases but it's not something we've recommended. You can see https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration-providers#xml-configuration-provider for the type of XML files that are meant to be used with the XML configuration provider.

udlose commented 11 months ago

How would you suggest doing this?

Ok, I understand. How would you suggest doing what I need above then?

andy1547 commented 11 months ago

@udlose I can't speak for your specific use case, but I rolled my own configuration source capable of reading ASP.NET web.config files, to assist in incrementally migrating a ASP.NET WebAPI application to ASP.NET Core. I wanted avoid duplicating the same settings in both an appsettings.json (or appsettings.Production.json) and a web.config, so I setup the ASP.NET Core application to read from the ASP.NET web.config file, allowing these settings to be accessed through the IConfiguration interface. I've not tested it with an exe.config file (what app.config gets transformed to when building), but I suspect the same solution would work.

// usage in ASP.NET Core v6
var builder = WebApplication.CreateBuilder(args); // this will configure standard config sources
var pathToConfig = builder.Configuration["PathToConfig"]; // store path in appsettings.json or in another standard config source 
builder.Configuration.AddXmlConfiguration(pathToWebConfig, false, true); // up to you whether you make this file mandatory or optional

// classes
using Microsoft.Extensions.FileProviders;
using System.Configuration;
using ConfigurationManager = System.Configuration.ConfigurationManager;

public static class ConfigurationBuilderExtensions
{
    public static IConfigurationBuilder AddXmlConfiguration(
        this IConfigurationBuilder builder,
        string path)
        => AddXmlConfiguration(builder, provider: null, path: path, optional: false, reloadOnChange: false);

    public static IConfigurationBuilder AddXmlConfiguration(
        this IConfigurationBuilder builder,
        string path,
        bool optional)
        => AddXmlConfiguration(builder, provider: null, path: path, optional: optional, reloadOnChange: false);

    public static IConfigurationBuilder AddXmlConfiguration(
        this IConfigurationBuilder builder,
        string path,
        bool optional,
        bool reloadOnChange)
        => AddXmlConfiguration(builder, provider: null, path: path, optional: optional, reloadOnChange: reloadOnChange);

    public static IConfigurationBuilder AddXmlConfiguration(
        this IConfigurationBuilder builder,
        IFileProvider provider,
        string path,
        bool optional,
        bool reloadOnChange)
    {
        if (builder == null)
            throw new ArgumentNullException(nameof(builder));

        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentException("Must not be null/empty", nameof(path));

        return builder.AddXmlConfiguration(s =>
        {
            s.FileProvider = provider;
            s.Path = path;
            s.Optional = optional;
            s.ReloadOnChange = reloadOnChange;
            s.ResolveFileProvider();
        });
    }

    public static IConfigurationBuilder AddXmlConfiguration(
        this IConfigurationBuilder builder,
        Action<XmlConfigurationSource> configureSource)
    {
        var source = new XmlConfigurationSource();
        configureSource.Invoke(source);
        // insert with lowest preference, so other configuration sources (e.g. appsettings.json) with same setting name can override it
        builder.Sources.Insert(0, source);
        return builder;
    }
}

public sealed class XmlConfigurationSource : FileConfigurationSource, IConfigurationSource
{
    public override IConfigurationProvider Build(IConfigurationBuilder builder) => new XmlConfigurationProvider(this);
}

public sealed class XmlConfigurationProvider : FileConfigurationProvider
{
    public XmlConfigurationProvider(FileConfigurationSource source) : base(source) { }

    // TODO: instead of overriding FileConfigurationSource and throwing away stream,
    // This ideally should override ConfigurationProvider instead, however this requires reinventing the wheel
    public override void Load(Stream _)
    {
        var path = Source.FileProvider.GetFileInfo(Source.Path).PhysicalPath;

        ExeConfigurationFileMap map = new()
        {
            ExeConfigFilename = path
        };

        var config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

        Dictionary<string, string> data = new();
        foreach (var key in config.AppSettings.Settings.AllKeys)
        {
            data.Add(key, config.AppSettings.Settings[key].Value);
        }
        // optional, also extract connection strings, using Azure format
        // https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-8.0&tabs=basicconfiguration#connection-string-prefixes
        foreach (var connectionString in config.ConnectionStrings.ConnectionStrings.Cast<ConnectionStringSettings>())
        {
            data.Add($"ConnectionStrings:{connectionString.Name}", connectionString.ConnectionString);
            data.Add($"ConnectionStrings:{connectionString.Name}_ProviderName", connectionString.ProviderName);
        }

        // perform atomic swap
        Data = data;
    }
}

Hope this helps.

thompson-tomo commented 8 months ago

I too am encountering this issue. I have a desktop app which we use to manage configuration files which could be xml or json. The idea was to use a standardised mechanism for reading the config regardless of source file type.

perahoky commented 4 months ago

AddXmlFile isn't meant for use with .NETFramework config files - it might work in some cases but it's not something we've recommended. You can see https://learn.microsoft.com/en-us/dotnet/core/extensions/configuration-providers#xml-configuration-provider for the type of XML files that are meant to be used with the XML configuration provider.

hello,

yeah this might be, but please let it up to us whether we use config xml files via the default xmlfileprovider. Even if it doesnt support all the features, we dont care. in tthe end of the day its still just the same xml specifications, just without the runtime features of configurationmanager. there is still no reason to raise hard exception for legally defined namespaces in xml files, even if they are not supported. Put a flag or something to indicate "namespace is not supported". Its annoying to get such random exceptions.