C# 如何从数据库中的XML读取配置节?
我有这样一个配置类:C# 如何从数据库中的XML读取配置节?,c#,.net,.net-3.5,configuration,C#,.net,.net 3.5,Configuration,我有这样一个配置类: public class MyConfig : ConfigurationSection { [ConfigurationProperty("MyProperty", IsRequired = true)] public string MyProperty { get { return (string)this["MyProperty"]; } set { this["MyPro
public class MyConfig : ConfigurationSection
{
[ConfigurationProperty("MyProperty", IsRequired = true)]
public string MyProperty
{
get { return (string)this["MyProperty"]; }
set { this["MyProperty"] = value; }
}
}
public class ConfigurationSectionReader where T : ConfigurationSection, new()
{
public T GetSection( string sectionXml )
{
T section = new T();
using ( StringReader stringReader = new StringReader( sectionXml ) )
using ( XmlReader reader = XmlReader.Create( stringReader, new XmlReaderSettings() { CloseInput = true } ) )
{
reader.Read();
section.GetType().GetMethod( "DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance ).Invoke( section, new object[] { reader, true } );
}
return section;
}
}
var reader = new ConfigurationSectionReader();
var section = reader.GetSection( sectionXml ); // where sectionXml is the XML string retrieved from the DB
它被另一个类实例化,就像这样
(MyConfig)ConfigurationManager.GetSection("myConfig")
我们正在进行一些更改,现在将配置文件作为xml存储在DB中,与当前在配置文件中完全相同
为了向后兼容,我希望将MyConfig作为ConfigurationSection进行维护,但仍然能够使用从数据库检索的XML字符串对其进行实例化
可能吗?如果是,怎么做?(请记住,它仍应像上面的示例一样工作)这是一个难题。您可能会有一个配置文件,其中包含一些故意不正确的XML,在ConfigurationSection中覆盖未识别的删除,然后有效地绕过文件到ConfigurationSection的映射(本质上是手动设置属性)-需要进行一些重构,但您仍然可以公开相同的属性等。这有点WTF,但可能是可行的 我基本上描述了。在我所有的代码中,现在我没有依赖ConfigurationSection的类,我使用博客文章中描述的技术绕过它,通过接口返回POCOs。这使得我的代码更易于单元测试,因为我可以很容易地为接口使用存根 如果我希望这样做,我也可以轻松地将配置移动到DB中——我只需创建一个实现配置接口的新类,并在IoC配置中切换它。微软没有设计灵活的配置系统,所以在自己的代码中使用它时必须考虑到这一点
我能想到的唯一其他方法是将DB配置写入文件,然后读入,但这也很奇怪 我的建议是保留当前MyConfig类,但从构造函数中的数据库加载XML,然后在MyConfig的每个属性中,如果需要从任何位置提取配置,可以放入逻辑以确定从何处获取值(数据库或.config文件),或者,如果值为空,则将其回滚
public class MyConfig : ConfigurationSection
{
public MyConfig()
{
// throw some code in here to retrieve your XML from your database
// deserialize your XML and store it
_myProperty = "<deserialized value from db>";
}
private string _myProperty = string.Empty;
[ConfigurationProperty("MyProperty", IsRequired = true)]
public string MyProperty
{
get
{
if (_myProperty != null && _myProperty.Length > 0)
return _myProperty;
else
return (string)this["MyProperty"];
}
set { this["MyProperty"] = value; }
}
}
公共类MyConfig:ConfigurationSection
{
公共MyConfig()
{
//在这里输入一些代码,从数据库中检索XML
//反序列化XML并存储它
_myProperty=“”;
}
私有字符串_myProperty=string.Empty;
[ConfigurationProperty(“MyProperty”,IsRequired=true)]
公共字符串MyProperty
{
得到
{
如果(_myProperty!=null&&u myProperty.Length>0)
归还我的财产;
其他的
返回(字符串)此[“MyProperty”];
}
设置{this[“MyProperty”]=value;}
}
}
我通常是这样做的:只需将这些成员添加到MyConfig类中:
public class MyConfig : ConfigurationSection
{
private static MyConfig _current;
public static MyConfig Current
{
get
{
if (_current == null)
{
switch(ConfigurationStorageType) // where do you want read config from?
{
case ConfigFile: // from .config file
_current = ConfigurationManager.GetSection("MySectionName") as MyConfig;
break;
case ConfigDb: // from database
default:
using (Stream stream = GetMyStreamFromDb())
{
using (XmlTextReader reader = new XmlTextReader(stream))
{
_current = Get(reader);
}
}
break;
}
}
return _current;
}
}
public static MyConfig Get(XmlReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
MyConfig section = new MyConfig();
section.DeserializeSection(reader);
return section;
}
}
这样,MyConfig类中没有什么可更改的,但您仍然需要更改客户使用此类代码访问它的方式:
string myProp = MyConfig.Current.MyProperty;
如果您需要在数据库中存储任何<强>系统.SoadsCuffice部分< /St>,您可以考虑编写这样的泛型节读取器:
public class MyConfig : ConfigurationSection
{
[ConfigurationProperty("MyProperty", IsRequired = true)]
public string MyProperty
{
get { return (string)this["MyProperty"]; }
set { this["MyProperty"] = value; }
}
}
public class ConfigurationSectionReader where T : ConfigurationSection, new()
{
public T GetSection( string sectionXml )
{
T section = new T();
using ( StringReader stringReader = new StringReader( sectionXml ) )
using ( XmlReader reader = XmlReader.Create( stringReader, new XmlReaderSettings() { CloseInput = true } ) )
{
reader.Read();
section.GetType().GetMethod( "DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance ).Invoke( section, new object[] { reader, true } );
}
return section;
}
}
var reader = new ConfigurationSectionReader();
var section = reader.GetSection( sectionXml ); // where sectionXml is the XML string retrieved from the DB
这将适用于重写反序列化元素方法的所有类。e、 g
protected override void DeserializeElement( XmlReader reader, bool serializeCollectionKey )
{
XmlDocument document = new XmlDocument();
document.LoadXml( reader.ReadOuterXml() );
MyProperty = document.DocumentElement.HasAttribute( "MyProperty" )
? document.DocumentElement.Attributes[ "MyProperty" ].Value
: string.Empty;
}
你可以得到这样的一个部分:
public class MyConfig : ConfigurationSection
{
[ConfigurationProperty("MyProperty", IsRequired = true)]
public string MyProperty
{
get { return (string)this["MyProperty"]; }
set { this["MyProperty"] = value; }
}
}
public class ConfigurationSectionReader where T : ConfigurationSection, new()
{
public T GetSection( string sectionXml )
{
T section = new T();
using ( StringReader stringReader = new StringReader( sectionXml ) )
using ( XmlReader reader = XmlReader.Create( stringReader, new XmlReaderSettings() { CloseInput = true } ) )
{
reader.Read();
section.GetType().GetMethod( "DeserializeElement", BindingFlags.NonPublic | BindingFlags.Instance ).Invoke( section, new object[] { reader, true } );
}
return section;
}
}
var reader = new ConfigurationSectionReader();
var section = reader.GetSection( sectionXml ); // where sectionXml is the XML string retrieved from the DB
这是一个很老的问题,但只是在玩弄解决方案。它类似于Simon Mourier的方法(在某些方面我更喜欢这种方法——少一些黑客行为),但确实意味着调用
System.Configuration.ConfigurationManager.GetSection()
的任何代码都将继续工作,而无需更改它们以使用静态方法,因此可能会导致总体代码更改较少
第一个基本的警告是,我不知道这是否适用于嵌套部分,但我几乎可以肯定它不会。几乎主要的警告是,它需要更改config section类,因此您只能将其用于您有源代码的自定义节(并且允许更改!)
第二个也是主要的警告是,我只是在玩弄它,我在开发和生产中都没有使用它,简单地在基本功能上涂抹我自己的代码,就像这样,可能会产生我的示例中没有显示的连锁反应使用风险自担
(话虽如此,我正在一个Umbraco站点上测试它,因此其他配置部分的负载仍在继续,它们仍然可以工作,所以我认为它不会立即产生可怕的影响)
编辑:这是.NET4,而不是原始问题中的3.5。不知道这会不会有什么不同
下面是代码,非常简单,只需重写反序列化部分
即可使用从数据库加载的XML读取器
public class TestSettings : ConfigurationSection
{
protected override void DeserializeSection(System.Xml.XmlReader reader)
{
using (DbConnection conn = /* Get an open database connection from whatever provider you are using */)
{
DbCommand cmd = conn.CreateCommand();
cmd.CommandText = "select ConfigFileContent from Configuration where ConfigFileName = @ConfigFileName";
DbParameter p = cmd.CreateParameter();
p.ParameterName = "@ConfigFileName";
p.Value = "TestSettings.config";
cmd.Parameters.Add(p);
String xml = (String)cmd.ExecuteScalar();
using(System.IO.StringReader sr = new System.IO.StringReader(xml))
using (System.Xml.XmlReader xr = System.Xml.XmlReader.Create(sr))
{
base.DeserializeSection(xr);
}
}
}
// Below is all your normal existing section code
[ConfigurationProperty("General")]
public GeneralElement General { get { return (GeneralElement)base["General"]; } }
[ConfigurationProperty("UI")]
public UIElement UI { get { return (UIElement)base["UI"]; } }
...
...
}
我使用的是ASP.Net,所以为了让它正常工作,您确实需要一个web.config
,但是,无论如何,我需要一个连接字符串的地方,否则我根本不打算连接到数据库
您的自定义节应在
中定义为普通节;实现这一点的关键是在你的正常设置中加入一个空元素;i、 e.代替
或您的内联设置,只需将
然后,configuration manager将加载所有部分,查看现有的
元素,并对其进行反序列化,此时它将点击您的覆盖并从数据库中加载XML
注意:反序列化需要一个文档片段(当读卡器已位于某个节点时,需要调用它),而不是整个文档,因此如果您的节存储在单独的文件中,则必须首先删除
声明,或者你得到期望找到一个元素
我很想看到一个解决方案-我已经研究了一段时间,不幸的是没有太大的成功…我很高兴看到我不是一个人。我不知道这个问题的答案,因为似乎应用程序代码无法更改配置文件的位置。但我不会