我在我的应用程序中有一个自定义配置属性,它看起来像这样:
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
}
然而,我想要的是允许.config文件指向外部文件源。像这样:
<ServiceOverrides file="Overrides.local.config" />
现在,内置的configSource
属性与我需要的接近,但是它有两个主要问题。- 文件必须存在如果文件不存在,就会出错。
- 文件必须在当前目录或更深的目录中。换句话说,我不能指向
..Overrides.local.config
我想要的与<appSettings file="..." />
配置元素几乎相同。然而,该属性似乎是appSettings
实现的东西,而不是基本ConfigurationSection
类的一部分。
我的问题:
是否有可能在ConfigurationSection
中重写一些东西,基本上会从不同的位置读取XML数据?我不想改变我的类的任何其他方面,也不想做我自己的XML反序列化或任何事情。我只是想检查一个文件是否存在,如果存在,从文件加载XML内容,否则加载默认XML内容。
好了,我有一个可行的解决方案。我不确定这是否是最好的方法,但它确实看起来像我想要的那样工作。
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);
}
}
首先,我捕获DeserializeElement
事件,该事件在读取<ServiceOverrides>
元素时发生。我们检查它是否有File
属性,如果有,我们将它添加到一个外部源队列中来加载。
接下来,我们捕获PostDeserialize
事件,该事件在解析完所有本地XML后被调用。如果队列中有外部源,我们将它从队列中取出,检查它是否确实存在,然后用该文件的内容创建一个XmlReader
。现在我们可以简单地再次调用DeserializeSection
并传入我们的新阅读器。ConfigurationSection
类非常聪明,可以直接覆盖或将添加到现有配置中的任何新数据。我最后得到的是两个配置文件的聚合,其中include文件在重复的情况下胜出。
这个队列是怎么回事?似乎每次调用DeserializeSection
,它都会再次调用PostDeserialize
。因此,如果我们简单地捕获PostDeserialize
,检查File
属性,并再次调用DeserializeSection
,我们将进入无限循环。我们可以使用标志来记住我们是否已经加载了外部文件,但是队列有一个额外的好处,允许包含文件加载更多的包含文件(并不是说我想这样做,但你可能会这样做)。
提示:这可能会很好地工作,并且很容易理解,但如果您在生产代码中使用它,则有一些事情可以改进。首先,externalSources
不需要真正的是一个队列,因为这些调用实际上不是递归的。您可能只使用string
,并在处理完该文件后将其设置为null。其次,在循环包含链的情况下,这可能导致无限循环。您可以为先前包含的文件创建一个List<T>
,然后在将其添加到队列之前检查该包含是否已经存在于该列表中。
希望这能帮助到一些人!