简体   繁体   English

是否可以从外部文件初始化ConfigurationSection?

[英]Is it possible to initialize a ConfigurationSection from an external file?

I have a custom configuration property in my app. 我的应用程序中有一个自定义配置属性。 It looks something like this: 看起来像这样:

public class OverrideConfiguration : ConfigurationSection
{
    [ConfigurationProperty(PROP_ADMIN_CONNSTR)]
    public StringConfigurationElement AdminConnectionString
    {
        get { return base[PROP_ADMIN_CONNSTR] as StringConfigurationElement; }
        set { base[PROP_ADMIN_CONNSTR] = value; }
    }

    // .. Various other properties, but you get the idea
}

However, what I'd like is to allow the .config file to be pointed to an external file source. 但是,我要允许.config文件指向外部文件源。 Something like this: 像这样:

<ServiceOverrides file="Overrides.local.config" />

Now, the built-in configSource attribute is close to what I need, but it has two major issues. 现在,内置的configSource属性接近我需要的属性,但是有两个主要问题。

  1. Files must exist. 文件必须存在。 If the file doesn't exist, it errors out. 如果文件不存在,则会出错。
  2. Files must be in the current directory or in a deeper directory. 文件必须位于当前目录或更深的目录中。 In other words, I can't point to ..\\Overrides.local.config 换句话说,我无法指向..\\Overrides.local.config

What I want is pretty much identical to the <appSettings file="..." /> configuration element. 我想要的与<appSettings file="..." />配置元素几乎相同。 However, that attribute seems to be something appSettings implemented, and is not part of the base ConfigurationSection class. 但是,该属性似乎是由appSettings实现的,并且不是基本ConfigurationSection类的一部分。

My Question: 我的问题:

Is it possible to override something in ConfigurationSection that will basically read XML data from a different location? 是否有可能重写ConfigurationSection中的某些内容,这些内容基本上将从其他位置读取XML数据? I don't want to change any other aspect of my class or do my own XML deserialization or anything. 我不想更改类的任何其他方面,也不想做自己的XML反序列化或任何其他事情。 I simply want to check if a file exists, if so, load in the XML contents from that file, otherwise load in the default XML contents. 我只想检查文件是否存在,如果存在,则从文件中加载XML内容,否则加载默认XML内容。

Ok, I have a working solution. 好的,我有一个可行的解决方案。 I'm not sure if it's the best approach, but it does appear to work exactly how I want. 我不确定这是否是最好的方法,但是它确实可以按照我的要求工作。

private readonly Queue<String> externalSources = new Queue<String>();

protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
{
    var externalFile = reader.GetAttribute("File");
    if(!String.IsNullOrWhiteSpace(externalFile))
    {
        externalSources.Enqueue(externalFile);
    }

    base.DeserializeElement(reader, serializeCollectionKey);
}

protected override void PostDeserialize()
{
    base.PostDeserialize();

    // Override data with local stuff
    if (externalSources.Count == 0) return;

    string file = externalSources.Dequeue();
    if (System.IO.File.Exists(file))
    {
        var reader = XmlReader.Create(file);
        base.DeserializeSection(reader);
    }
}

First, I trap the DeserializeElement event, which happens when we read the <ServiceOverrides> element. 首先,我捕获DeserializeElement事件,该事件在我们读取<ServiceOverrides>元素时发生。 We check if it has a File attribute, and if so we add it to a queue of external sources to load. 我们检查它是否具有File属性,如果是,我们将其添加到要加载的外部源队列中。

Next, we trap the PostDeserialize event, which gets called after all the local XML is parsed. 接下来,我们捕获PostDeserialize事件,该事件在解析所有本地XML之后被调用。 If there's an external source in the queue, we dequeue it, check if it actually exists, then create an XmlReader with the contents of that file. 如果队列中有外部源,我们将其出队,检查它是否确实存在,然后使用该文件的内容创建一个XmlReader Now we can simply call DeserializeSection again and pass in our new reader. 现在我们可以简单地再次调用DeserializeSection并传入新的阅读器。 The ConfigurationSection class is smart enough to just overwrite or append any new data to the existing configuration. ConfigurationSection类足够聪明,可以任何新数据覆盖附加到现有配置中。 What I get at the end is an aggregation of both configuration files, where the include file wins in the event of a duplicate. 最后我得到的是两个配置文件的聚合,其中包含文件在重复的情况下胜出。

Now, what's this nonsense with the queue? 现在,队列有什么废话? Well, it seems every time you call DeserializeSection , it'll call PostDeserialize again. 好吧,似乎每次您调用DeserializeSection ,它都会再次调用PostDeserialize So, if we simply trapped PostDeserialize , check the File attribute, and call DeserializeSection again, we'd get in an infinite loop. 因此,如果我们简单地捕获PostDeserialize ,检查File属性,然后再次调用DeserializeSection ,我们将陷入无限循环。 We could just use a flag to remember if we already loaded the external file, but a queue has the added benefit of allowing the include file to load more include files (not that I'd ever want to do that, but you might). 我们可以仅使用一个标志来记住是否已经加载了外部文件,但是队列的另一个好处是允许包含文件加载更多的包含文件(不是我想这样做,但您可以这样做)。

Tips: This will probably work fairly well, and is simple to understand, but if you're using it in production code, there's a few things you could improve on. 提示:这可能会很好地工作,并且很容易理解,但是如果您在生产代码中使用它,则可以在某些方面进行改进。 First, externalSources doesn't really need to be a queue, since these calls aren't actually recursive. 首先, externalSources并不真正需要的是一个队列,因为这些电话实际上并不是递归的。 You can probably just use a string , and set it to null after you're done processing that file. 在处理完该文件之后,您可以只使用一个string ,并将其设置为null。 Second, this could cause an infinite loop in the event of a circular include chain. 其次,这可能在循环包含链的情况下导致无限循环。 You could create a List<T> of previously included files, then check if the include already exists in that list before adding it to the queue. 您可以创建先前包含的文件的List<T> ,然后在将其添加到队列之前检查该列表中是否已包含该文件。

Hope this helps someone! 希望这对某人有帮助!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM