ASP.NET MVC. Конфигурационный файл Web.config. Часть 2. Настройка и использование

Дата публикации: 08.07.2017. Категория: ASP.NET MVC
Последнее обновление: 17.01.2020

Это вторая часть урока по теме конфигурационного файла Web.config. В этой части урока мы перейдем к практике и попробуем управлять настройками нашего веб-приложения через этот файл.

< Перейти к первой части урока

Для начала давайте познакомимся со специальным классом WebConfigurationManager. Давайте перейдем к определению этого класса, и посмотрим из чего он состоит.  Этот класс находится в пространстве имен System.Web.Configuration. Мы видим определенный набор свойств и методов, часть из которых перегружена. Класс довольно простой.

Для работы с конфигурационным файлом Web.config мы будем использовать следующие его члены:

AppSettings – это свойство возвращает стандартную NameValueCollection коллекцию, которая представляет собой набор простых настроек приложения. Обращаясь по ключу или по индексу, можно получить значение определенного элемента в секции appSettings в файле Web.config.

ConnectionStrings – это свойство возвращает коллекцию объектов класса ConnectionStringSettings. Данный класс описывает конкретную строку подключения к внешнему источнику данных, например, к базе данных.

GetWebApplicationSection(section) – этот метод возвращает объект, с помощью которого можно получить информацию об определенной секции в конфигурационном файле, и уже как-то работать с ее конкретными атрибутами.

namespace System.Web.Configuration
{
    public static class WebConfigurationManager
    {
        public static NameValueCollection AppSettings { get; }
        public static ConnectionStringSettingsCollection ConnectionStrings { get; }
        public static object GetWebApplicationSection(string sectionName);
        //другие члены...
        //....
    }
}

Теперь давайте попробуем внести изменения в файл Web.config. Предположим, что нам нужно добавить свои собственные настройки в приложение. Мы уже знаем, что жестко закодировать новые настройки, например, через константы – это не самая лучшая идея. Поэтому добавим их в конфигурационный файл Web.config.

Например, в нашем проекте мы будем работать с различными странами, городами и часовыми поясами. Нам нужно добавить город по умолчанию и часовой пояс по умолчанию, чтобы в случае чего эти данные могли быть использованы в процессе работы.

В этом случае, когда новый элемент конфигурации не представляет собой сложную логическую структуру, мы обратимся к секции appSettings. В этой секции размещены простые элементы конфигурации типа ключ\значение. Давайте здесь же добавим новые два элемента – город по умолчанию и часовой пояс по умолчанию:

    <add key="defaultCity" value="Moscow"/>
    <add key="defaultUtcTimeZoneShift" value="3"/>

Теперь посредством класса WebConfigurationManager мы можем обратиться к секции appSettings и прочитать новые настройки:

var settings = WebConfigurationManager.GetWebApplicationSection("appSettings");
//или
NameValueCollection config = WebConfigurationManager.AppSettings;
foreach (string key in config)
{
    System.Diagnostics.Debug.WriteLine(string.Format("{0} - {1}", key, config[key]));
}

Таким образом, секция appSettings в файле Web.config – это идеальное место для объявления простых настроек в нашем приложении, которые можно описать правилом «ключ\значение».

Однако с этим подходом есть одна проблема. Если мы захотим описать более сложную конфигурационную модель, то нам придется каждую новую настройку записывать отдельно в виде пары ключ\значение. Из-за этого файл Web.config может стать очень большим и работать с ним станет неудобно.

Давайте попробуем создать более сложную структуру, подобную группе system.web. То есть мы объявим новую группу, в которую будут вложены отдельные элементы с нужными атрибутами:

<userCustomSettings>
  <defaultValues email="admin@admin.ru" city="Moscow" timeZoneShift="3" />
</userCustomSettings>
<connectionStrings>

Работа с конфигурацией такого типа немного сложнее. Прежде чем мы сможем использовать ее в работе, нам придется написать обслуживающий код. Нам понадобится создать классы-обработчики, описывающие эту конфигурацию программно, а также зарегистрировать ее в файле Web.config.

namespace WebConfigPractice.ServiceCode
{
    public class DefaultValuesConfigSection : ConfigurationSection
    {
        [ConfigurationProperty("email")]
        public string Email
        {
            get { return (string)this["email"]; }
            set { this["email"] = value; }
        }

        [ConfigurationProperty("city")]
        public string City
        {
            get { return (string)this["city"]; }
            set { this["city"] = value; }
        }

        [ConfigurationProperty("timeZoneShift")]
        public int TimeZoneShift
        {
            get { return (int)this["timeZoneShift"]; }
            set { this["timeZoneShift"] = value; }
        }
    }
}

То есть, мы наследуемся от класса ConfigurationSection, а внутри нашего класса ассоциируем свойства с соответствующими атрибутами в секции.

Далее нам нужно подобным образом описать объявленную нами группу userCustomSettings, в которую вложен элемент defaultValues. Опять создаем класс обработчик, в этот раз для группы:

namespace WebConfigPractice.ServiceCode
{
    public class UserCustomSettingsConfigGroup : ConfigurationSectionGroup
    {
        public DefaultValuesConfigSection DefaultValues
        {
            get { return (DefaultValuesConfigSection) Sections["defaultValues"]; }
        }
    }
}

После этого нужно зарегистрировать объявленную группу и секцию в файле Web.config в разделе configSections:

<configSections>
  <sectionGroup name="userCustomSettings" type="WebConfigPractice.ServiceCode.UserCustomSettingsConfigGroup">
    <section name="defaultValues" type="WebConfigPractice.ServiceCode.DefaultValuesConfigSection"/>
  </sectionGroup>
</configSections>

Теперь все подготовительные действия закончены, и мы можем прочитать нашу конфигурацию:

UserCustomSettingsConfigGroup model = (UserCustomSettingsConfigGroup)WebConfigurationManager.OpenWebConfiguration("/").GetSectionGroup("userCustomSettings");

Кстати замечу, что для того чтобы работать со стандартными секциями и группами из файла Web.config, нам не придется для каждой из них писать класс-обработчик. Эти классы уже существуют, их можно использовать для работы, они находятся в пространстве имен System.Web.Configuration.

Благодаря тому, что мы описали нашу конфигурацию через классы-обработчики, теперь мы можем программно контролировать эту конфигурацию. Во-первых, мы можем дописать специальные параметры к конфигурационным атрибутам:

namespace WebConfigPractice.ServiceCode
{
    public class DefaultValuesConfigSection : ConfigurationSection
    {
        [ConfigurationProperty("email")]
        //...
        [ConfigurationProperty("city", IsRequired = true)]
        //...
        [ConfigurationProperty("timeZoneShift", DefaultValue = 5)]
        //...
    }
}

Теперь, если мы удалим из конфигурации атрибут timeZoneShift, то будет использовано его значение по умолчанию - 5. В то же время, если мы удалим атрибут city (который теперь стал обязательным), то увидим ошибку конфигурации, и наше приложение не сможет стартануть.

То есть через параметры мы можем добиться того, что перед стартом приложения конфигурация должна быть корректно настроена. Иногда это может быть очень важно. Гораздо безопаснее будет просто не дать приложению запуститься, чем потом в процессе работы ломать голову и искать какую-нибудь логическую ошибку из-за отсутствующего значения города.

Также помимо этих параметров мы можем применить дополнительные валидационные атрибуты к нашим свойствам, описывающим секцию в конфиге. Ниже представлен список всех доступных атрибутов:

CallbackValidator - используется, чтобы реализовать пользовательскую валидацию (см.ниже).

StringValidator - используется, чтобы реализовать простую валидацию над строковыми значениями. Например, ограничить длину строки.

IntegerValidator - используется, чтобы реализовать валидацию над значениями типа int. Например, ограничить число некоторым диапазоном.

LongValidator - используется для валидации значений типа long.

TimeSpanValidator - используется для валидации TimeSpan значений. Например, также можно ограничить диапазон значений.

RegexStringValidator - можно использовать для проверки, подходит ли строка под регулярное выражение или нет.

Одним из наиболее интересных атрибутов является атрибут CallbackValidator. Он позволяет нам произвести проверку значения по нашим собственным условиям. Мы объявляем метод, в котором будет проводиться проверка, передаем название этого метода в валидатор, и в момент загрузки конфигурации приложения этот метод будет вызван для проверки значения в конфигурации.

Давайте напишем метод, в котором сделаем проверку, что если в представленном email нет слова admin, то тогда выбрасывать исключение, что представленный емаил нам не подходит:

namespace WebConfigPractice.ServiceCode
{
    public class DefaultValuesConfigSection : ConfigurationSection
    {
        [ConfigurationProperty("email")]
        [CallbackValidator(CallbackMethodName = "ValidateConfigEmail", Type = typeof(DefaultValuesConfigSection))]
        public string Email
        {
            get { return (string)this["email"]; }
            set { this["email"] = value; }
        }
        //...

        public static void ValidateConfigEmail(object val)
        {
            if (string.IsNullOrEmpty((string) val))
                return;
            if (!((string)val).ToLower().Contains("admin"))
                throw new Exception("Указан невалидный Email");
        }
    }
}

Обратите внимание на важный момент. Особенность работы валидационных атрибутов такова, что валидаторы вызываются дважды за проверку. Первый раз в валидатор передается значение по умолчанию для данного типа (пустая строка для типа string, 0 для типа int и т.д.). Далее происходит второй вызов валидатора, и уже передается значение из файла Web.config. Именно поэтому мы сперва делаем проверку на пустую строку в конструкции if. Если не обработать двойной вызов валидатора должным образом, то наша проверка будет завершаться неудачно.

Давайте также посмотрим, как мы можем работать с указанными в конфигурации строками подключения к внешним источникам данных. Для начала представим, что у нас в приложении объявлена следующая строка подключения к базе данных:

<connectionStrings>
  <remove name="LocalSqlServer" />
  <add name="LocalSqlServer" providerName="System.Data.SqlClient" connectionString="Data Source=(local); Database=TestDb; User ID=sa; Password=sa" />
</connectionStrings>

Для того, чтобы работать со строками подключения, указанными в конфигурации, мы обращаемся к классу WebConfigurationManager, к свойству ConnectionStrings. Это свойство возвращает объект класса ConnectionStringSettingsCollection, который содержит коллекцию всех объявленных строк подключения:

ConnectionStringSettingsCollection model = WebConfigurationManager.ConnectionStrings;
System.Diagnostics.Debug.WriteLine(model[0].ProviderName);

Как видно, работать со строками подключения очень просто и удобно. Для этого в классе WebConfigurationManager определено специальное свойство.

Нам осталось рассмотреть вопрос переопределения конфигурации для какой-то части приложения. Это можно сделать двумя способами:

  • через элемент location в файле Web.config
  • через вложенный файл Web.config

Элемент location позволяет нам определить отдельные секции в конфигурационном файле, которые будут задействованы при обращении по определенному URL. Например, добавим особые правила для действия Index в контроллере Home:

<location path="Home/Index">
  <appSettings>
    <add key="defaultCity" value="New York"/>
  </appSettings>
</location>

Теперь при обращении по этому URL объявленная нами конфигурация будет переопределена новыми значениями. Обратите внимание, что другие элементы секции appSettings были унаследованы от родительской конфигурации в файле Web.config.

Однако использовать данный подход следует с осторожностью, либо не использовать вовсе. Все дело в системе маршрутизации нашего приложения. Если у вас определены какие-то другие маршруты помимо стандартного, то явное указание URL-адреса в элементе location может стать проблемой. Обратите на этом внимание.

Также мы можем создать дополнительный файл Web.config в поддиректории нашего приложения. Например, создадим конфигурационный файл в папке Views/Home:

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="defaultCity" value="New York"/>
  </appSettings>
</configuration>

Для того, чтобы применить новые настройки из вложенного файла Web.config, нам следует прочитать эту конфигурацию через WebConfigurationManager:

AppSettingsSection settings = WebConfigurationManager.OpenWebConfiguration("~/Views/Home").AppSettings;

Также хочу заметить, что мы можем не только получать информацию о текущих настройках приложения, но и изменять настройки приложения в runtime. Например:

WebConfigurationManager.AppSettings["defaultCity"] = "New York";

В большинстве случаев делать это не рекомендуется, так как подобные действия могут приводить к различным ошибкам в приложении в процессе выполнения, к потере данных и так далее.

На этом здесь все. Мы изучили структуру конфигурационных файлов сервера и конкретного веб-приложения, научились читать конфигурацию и добавлять собственные настройки, посмотрели на преимущества файла Web.config.

Вернуться наверх
наверх