Я пытаюсь использовать определения сущностей в файле конфигурации, чтобы упростить различия между разработкой, QA, UAT и рабочей версией. Вот пример начала моего конфигурационного файла:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration [
<!ENTITY MyStorageLocation "\\MyServer\MyStorageFolder">
<!ENTITY MyDatabaseServer "devdb01">
]>
<configuration>
<configSections>
<section name="MyCustomSection" type="MyCustomSectionHandler,MyAssembly"/>
...
</configSections>
<connectionStrings>
<add name="MyConnectionString" providerName="System.Data.SqlClient" connectionString="Server=&MyDatabaseServer;;Database=MyDatabase;"/>
</connectionStrings>
...
<MyCustomSection>&MyStorageLocation;</MyCustomSection>
</configuration>
Кажется, это работает нормально, и почему бы и нет, поскольку это совершенно правильный XML, если я не использую ни одну из этих сущностей в пользовательском разделе конфигурации, для которого я в итоге вызываю ConfigurationManager.GetSection (). Использование сущности «MyDatabaseServer» в строке подключения не вызывает никаких проблем. В приведенном примере все будет работать нормально, пока я не использую сущность «MyStorageLocation» в элементе MyCustomSection, а затем я только сталкиваюсь с ошибкой при вызове ConfigurationManager.GetSection (), запрашивающем пользовательский раздел.
Мое лучшее предположение состоит в том, что класс ConfigurationManager берет необработанный источник элемента и пытается загрузить его как XML, игнорируя объявленные сущности для всего файла XML. Есть ли лучший способ сделать это, если не считать перекодирования пользовательских разделов конфигурации для поддержки ссылок на настройки вместо множества абсолютных настроек?
Ошибка, которую я получаю:
2009-01-27 14:00:53,474 [11936] ERROR MyCustomWindowsService [(null)] - Errors starting service -- shutting down
System.Configuration.ConfigurationErrorsException: Reference to undeclared entity 'MyStorageLocation'. Line 183, position 19. (D:\...\MyCustomWindowsService.exe.config line 183) ---> System.Xml.XmlException: Reference to undeclared entity 'MyStorageLocation'. Line 183, position 19.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.HandleGeneralEntityReference(String name, Boolean isInAttributeValue, Boolean pushFakeEntityIfNullResolver, Int32 entityStartLinePos)
at System.Xml.XmlTextReaderImpl.ResolveEntity()
at System.Xml.XmlTextReader.ResolveEntity()
at System.Xml.XmlLoader.LoadEntityReferenceNode(Boolean direct)
at System.Xml.XmlLoader.LoadNode(Boolean skipOverWhitespace)
at System.Xml.XmlLoader.LoadDocSequence(XmlDocument parentDoc)
at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
at System.Xml.XmlDocument.Load(XmlReader reader)
at System.Configuration.ErrorInfoXmlDocument.LoadFromConfigXmlReader(ConfigXmlReader reader)
at System.Configuration.RuntimeConfigurationRecord.RuntimeConfigurationFactory.CreateSectionImpl(RuntimeConfigurationRecord configRecord, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentConfig, ConfigXmlReader reader)
at System.Configuration.RuntimeConfigurationRecord.RuntimeConfigurationFactory.CreateSectionWithRestrictedPermissions(RuntimeConfigurationRecord configRecord, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentConfig, ConfigXmlReader reader)
at System.Configuration.RuntimeConfigurationRecord.CreateSection(Boolean inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentConfig, ConfigXmlReader reader)
at System.Configuration.BaseConfigurationRecord.CallCreateSection(Boolean inputIsTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentConfig, ConfigXmlReader reader, String filename, Int32 line)
--- End of inner exception stack trace ---
at System.Configuration.BaseConfigurationRecord.EvaluateOne(String[] keys, SectionInput input, Boolean isTrusted, FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult)
at System.Configuration.BaseConfigurationRecord.Evaluate(FactoryRecord factoryRecord, SectionRecord sectionRecord, Object parentResult, Boolean getLkg, Boolean getRuntimeObject, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSectionRecursive(String configKey, Boolean getLkg, Boolean checkPermission, Boolean getRuntimeObject, Boolean requestIsHere, Object& result, Object& resultRuntimeObject)
at System.Configuration.BaseConfigurationRecord.GetSection(String configKey, Boolean getLkg, Boolean checkPermission)
at System.Configuration.BaseConfigurationRecord.GetSection(String configKey)
at System.Configuration.ClientConfigurationSystem.System.Configuration.Internal.IInternalConfigSystem.GetSection(String sectionName)
at System.Configuration.ConfigurationManager.GetSection(String sectionName)
at MyCustomWindowsService.Monitor() in D:\...\MyCustomWindowsService.cs:line 207
at MyCustomWindowsService.Start() in D:\...\MyCustomWindowsService.cs:line 178
Глубоко в недрах ConfigurationManager ...