DTD Entities в конфигурационном файле - PullRequest
3 голосов
/ 27 января 2009

Я пытаюсь использовать определения сущностей в файле конфигурации, чтобы упростить различия между разработкой, 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 ...

1 Ответ

0 голосов
/ 09 мая 2009

Возможно, ваш XML-процессор игнорирует объявления! ENTITY, поскольку они содержатся в пустом DTD.

У вас есть DTD, который вы могли бы использовать для проверки этого документа? Не имея DTD или схемы, вы не можете сказать, что документ действителен. Попробуйте запустить его через онлайн-сервис проверки XML, например, URL-адрес: http://www.validome.org/xml/validate/

Вот исправленная версия вашего документа с DTD, которую я поднял. Посмотрите, поможет ли это.


<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE configuration [
    <!ELEMENT configuration (configSections+, connectionStrings+, MyCustomSection)>

    <!ELEMENT configSections (section+)>

    <!ELEMENT section EMPTY>
    <!ATTLIST section name CDATA #REQUIRED>
    <!ATTLIST section type CDATA #REQUIRED>

    <!ELEMENT connectionStrings (add+)>

    <!ELEMENT add EMPTY>
    <!ATTLIST add name CDATA #REQUIRED>
    <!ATTLIST add providerName CDATA #REQUIRED>
    <!ATTLIST add connectionString CDATA #REQUIRED>

    <!ELEMENT MyCustomSection (#PCDATA)>

    <!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>
...