Конфигурация .NET (app.config / web.config / settings.settings)

162

У меня есть приложение .NET, в котором есть разные файлы конфигурации для сборок Debug и Release. Например, файл debug app.config указывает на SQL-сервер разработки, на котором включена отладка, а цель выпуска указывает на работающий SQL Server. Есть и другие настройки, некоторые из которых отличаются в отладке / выпуске.

В настоящее время я использую два отдельных файла конфигурации (debug.app.config и release.app.config). У меня есть событие сборки проекта, в котором говорится, что если это сборка выпуска, скопируйте файл release.app.config в app.config, в противном случае скопируйте debug.app.config в app.config.

Проблема в том, что приложение, похоже, получает свои настройки из файла settings.settings, поэтому мне нужно открыть settings.settings в Visual Studio, который затем предлагает мне изменить настройки, чтобы я принял изменения, сохранил settings.settings и чтобы восстановить его, используйте правильные настройки.

Есть ли лучший / рекомендуемый / предпочтительный метод для достижения аналогичного эффекта? Или в равной степени я подошел к этому совершенно неправильно и есть ли лучший подход?

Gavin
источник
Я хочу отключить отладку в Windows, я попытался снять все флажки в настройках отладки, но все же я мог бы отладить исполняемый
файл выпусков

Ответы:

62

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

Это типы элементов конфигурации, которые я обычно храню на уровне машины:

Когда каждая среда (разработчик, интеграция, тестирование, этап, живая версия ) имеет свои уникальные параметры в каталоге c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , вы можете продвигать код своего приложения между средами без каких-либо модификации после сборки.

И очевидно, что содержимое каталога CONFIG на уровне машины получает управление версиями в другом хранилище или структуре папок, отличной от вашего приложения. Вы можете сделать ваши файлы .config более удобными для управления исходным кодом с помощью интеллектуального использования configSource .

Я занимаюсь этим уже 7 лет, работая над более чем 200 приложениями ASP.NET в более чем 25 компаниях. (Не пытаясь похвастаться, просто хочу сообщить, что я никогда не видел ситуации, когда этот подход не работает.)

Портман
источник
3
Как насчет ситуации, когда вы не контролируете веб-сервер и, следовательно, не можете изменить конфигурацию на уровне машины? Примеры включают сторонний веб-сервер или веб-сервер, совместно используемый несколькими подразделениями на предприятии.
RationalGeek
1
Не будет работать Но в эпоху виртуальных машин, Amazon EC2 и 400-долларовых серверов от Dell, кто-нибудь действительно делает что-нибудь серьезное на общих машинах? Не пытаясь быть черствым - я действительно думаю, что если вы работаете на общем веб-сервере, вам следует пересмотреть.
Портман
7
Большинство корпораций, в которых я работал с внутренними сайтами, размещают несколько приложений на одном сервере - там необходимо провести переоценку на корпоративном уровне
MPritchard
Несколько приложений на одном сервере хорошо, если все приложения находятся в одной «среде». Т.е. вам не нужен LIVE-экземпляр App1 на том же сервере, что и DEV-экземпляр App2. Например, ваши настройки SMTP будут общими для всех ваших приложений. В производстве вы указываете на реальный почтовый сервер; в процессе разработки вы указываете на файл на диске.
Портман
7
Я знаю, что это будет работать, но это все равно идет вразрез с тем, что я бы порекомендовал при попытке автоматизировать развертывание. Я думаю, что настройки зависят от приложения, они должны контролироваться версией вместе с приложением и развиваться вместе с ним. Опираясь на конфигурацию машины просто меняется, на мой взгляд, это усложняет. Мне нравится хранить вместе вещи, которые меняются вместе, и использовать их вместе. Если я добавлю новую настройку для Dev, мне, вероятно, понадобится эквивалентная настройка для prod.
Мигель Мадеро
52

Это может помочь некоторым людям, имеющим дело с Settings.settings и App.config: обратите внимание на атрибут GenerateDefaultValueInCode на панели свойств при редактировании любого значения в сетке Settings.settings в Visual Studio (Visual Studio 2008 в моем случае).

Если вы установите для GenerateDefaultValueInCode значение True (True здесь по умолчанию!), Значение по умолчанию компилируется в EXE (или DLL), вы можете найти его встроенным в файл при открытии в текстовом редакторе.

Я работал над консольным приложением, и если я по умолчанию установил EXE-файл, приложение всегда игнорировало файл конфигурации, находящийся в том же каталоге! Совсем кошмар и никакой информации об этом во всем интернете.

Римский
источник
7
Это именно то, что случилось со мной за прошедшие выходные. Я вырвал много волос, пытаясь понять, почему мое приложение, похоже, игнорирует мой файл app.config! Предполагается подключиться к веб-службе, а URL-адрес службы находится в моем app.config. Без моего ведома, когда я создал веб-ссылку, он также создал файл Settings.Settings И жестко запрограммировал значение по умолчанию в коде. Даже когда я наконец выяснил (и удалил) файл настроек, это значение по умолчанию осталось в жестком коде и было встроено в исполняемый файл. ОЧЕНЬ БЕСПОКОЙНО !! Благодаря этому посту теперь я могу избавиться от этой «фишки»
Майк К
+1 Этот ответ является критическим : если вы хотите, чтобы ваши настройки помещались в файл app.config, установите для его атрибута GenerateDefaultValueInCode значение False (по умолчанию установлено значение True).
Сабунку
34

Здесь есть связанный вопрос:

Улучшение вашего процесса сборки

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

<appSettings file="Local.config">

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

Если вы используете разделы конфигурации, эквивалент:

configSource="Local.config"

Конечно, рекомендуется создавать резервные копии всех файлов Local.config с других компьютеров и проверять их где-то, но не как часть реальных решений. Каждый разработчик помещает «ignore» в файл Local.config, чтобы он не регистрировался, что перезаписывает файл всех остальных.

(На самом деле вам не нужно называть это «Local.config», это именно то, что я использую)

Эрик З Борода
источник
14

Из того, что я читаю, похоже, что вы используете Visual Studio для процесса сборки. Задумывались ли вы об использовании MSBuild и Nant вместо этого?

Синтаксис Нанта в xml немного странный, но как только вы его поймете, делать то, что вы упомянули, становится довольно тривиально.

<target name="build">
    <property name="config.type" value="Release" />

    <msbuild project="${filename}" target="Build" verbose="true" failonerror="true">
        <property name="Configuration" value="${config.type}" />
    </msbuild>

    <if test="${config.type == 'Debug'}">
        <copy file=${debug.app.config}" tofile="${app.config}" />
    </if>

    <if test="${config.type == 'Release'}">
        <copy file=${release.app.config}" tofile="${app.config}" />
    </if>

</target>
Стивен Уильямс
источник
11

Мне кажется, что вы можете извлечь выгоду из Visual Studio 2005 Web Deployment Projects .

При этом вы можете указать ему обновить / изменить разделы вашего файла web.config в зависимости от конфигурации сборки.

Взгляните на эту запись в блоге от Скотта Гу для быстрого обзора / образца.

Магнус Йоханссон
источник
8

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

  <xmlpoke
    file="${stagingTarget}/web.config"
    xpath="/configuration/system.web/compilation/@debug"
    value="true"
  />

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

jasondoucette
источник
7

Мой нынешний работодатель решил эту проблему, сначала поместив уровень dev (отладка, stage, live и т. Д.) В файл machine.config. Затем они написали код, чтобы подобрать его и использовать правильный файл конфигурации. Это решило проблему с неверной строкой подключения после развертывания приложения.

Недавно они написали центральный веб-сервис, который возвращает правильную строку соединения из значения в значении machine.config.

Это лучшее решение? Наверное, нет, но у них это работает.

Гектор Соса-младший
источник
1
На самом деле я думаю, что это чертовски элегантно, так как мне нравится держать различные версии конфигурации видимыми внутри решения, даже если они не работают.
Annakata
1
Это очень интригующее решение. Хотелось бы рассмотреть пример этого в действии.
Майк К
5

Одним из решений, которое мне помогло, было использование WebDeploymentProject. У меня было 2/3 разных файлов web.config на моем сайте, и при публикации, в зависимости от выбранного режима конфигурации (выпуск / подготовка / и т. Д.), Я бы скопировал Web.Release.config и переименовал его в web. config в событии AfterBuild и удалите те, которые мне не нужны (например, Web.Staging.config).

<Target Name="AfterBuild">
    <!--Web.config -->
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Release.config" DestinationFiles="$(OutputPath)\Web.config" />
    <Copy Condition=" '$(Configuration)|$(Platform)' == 'Staging|AnyCPU' " SourceFiles="$(SourceWebPhysicalPath)\Web.Staging.config" DestinationFiles="$(OutputPath)\Web.config" />
    <!--Delete extra files -->
    <Delete Files="$(OutputPath)\Web.Release.config" />
    <Delete Files="$(OutputPath)\Web.Staging.config" />
    <Delete Files="@(ProjFiles)" />
  </Target>
Адам Виг
источник
3

У нашего проекта есть та же проблема, где мы должны были поддерживать конфиги для dev, qa, uat и prod. Вот что мы следовали (применимо, только если вы знакомы с MSBuild):

Используйте MSBuild с расширением задач MSBuild Community. Он включает в себя задачу «XmlMassUpdate», которая может «массово обновлять» записи в любом файле XML, как только вы дадите ему правильный узел для запуска.

Реализовать:

1) У вас должен быть один конфигурационный файл, в котором будут ваши записи dev env; это файл конфигурации в вашем решении.

2) У вас должен быть файл Substitutiontions.xml, который содержит только записи, РАЗНЫЕ (в основном appSettings и ConnectionStrings) для каждой среды. Записи, которые не изменяются в среде, не нужно помещать в этот файл. Они могут находиться в файле решения web.config и не будут затронуты задачей

3) В своем файле сборки просто вызовите задачу массового обновления XML и укажите подходящую среду в качестве параметра.

Смотрите пример ниже:

    <!-- Actual Config File -->
    <appSettings>
        <add key="ApplicationName" value="NameInDev"/>
        <add key="ThisDoesNotChange" value="Do not put in substitution file" />
    </appSettings>

    <!-- Substitutions.xml -->
    <configuration xmlns:xmu="urn:msbuildcommunitytasks-xmlmassupdate">
      <substitutions>
        <QA>
           <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInQA"/>
           </appSettings>            
        </QA>
        <Prod>
          <appSettings>
            <add xmu:key="key" key="ApplicationName" value="NameInProd"/>
          </appSettings>            
        </Prod>
     </substitutions>
    </configuration>


<!-- Build.xml file-->

    <Target Name="UpdateConfigSections">
            <XmlMassUpdate ContentFile="Path\of\copy\of\latest web.config" SubstitutionsFile="path\of\substitutionFile" ContentRoot="/configuration" SubstitutionsRoot="/configuration/substitutions/$(Environment)" />
        </Target>

замените «$ Environment» на «QA» или «Prod» в зависимости от того, что env. Вы строите для. Обратите внимание, что вы должны работать с копией файла конфигурации, а не с самим файлом конфигурации, чтобы избежать возможных ошибок, которые невозможно исправить.

Просто запустите файл сборки, а затем переместите обновленный файл конфигурации в среду развертывания, и все готово!

Для лучшего обзора прочитайте это:

http://blogs.microsoft.co.il/blogs/dorony/archive/2008/01/18/easy-configuration-deployment-with-msbuild-and-the-xmlmassupdate-task.aspx

Пунит Вора
источник
2

Как и вы, я также установил «multi» app.config - например, app.configDEV, app.configTEST, app.config.LOCAL. Я вижу некоторые из предложенных отличных альтернатив, но если вам нравится, как это работает для вас, я бы добавил следующее:

У меня есть
<appSettings>
<add key = "Env" value = "[Local] "/> для каждого приложения, я добавляю это в пользовательский интерфейс в заголовке: из ConfigurationManager.AppSettings.Get ("Env");

Я просто переименую конфигурацию в ту, на которую нацеливаюсь (у меня есть проект с 8 приложениями с большим количеством настроек базы данных / wcf против 4 событий). Для развертывания с clickonce в каждом я меняю 4 варианта в проекте и иду. (это я бы хотел автоматизировать)

Единственное, что мне нужно, это помнить «очистить все» после изменения, так как старый конфиг «застрял» после ручного переименования. (Который, я думаю, решит проблему с настройкой.)

Я считаю, что это работает очень хорошо (однажды у меня будет время взглянуть на MSBuild / NAnt)

Тони Трембат-Дрейк
источник
0

Web.config:

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

AppSetting.json:

Для всего остального, что не касается IIS, вы используете AppSetting.json. AppSetting.json используется для хостинга Asp.Net Core. ASP.NET Core использует переменную среды «ASPNETCORE_ENVIRONMENT» для определения текущей среды. По умолчанию, если вы запускаете свое приложение без установки этого значения, оно автоматически по умолчанию будет работать в производственной среде и использует файл «AppSetting.production.json». Когда вы отлаживаете через Visual Studio, он устанавливает среду разработки, поэтому он использует «AppSetting.json». Посетите этот сайт, чтобы понять, как установить переменную среды хостинга в Windows.

App.config:

App.config - это еще один файл конфигурации, используемый .NET, который в основном используется для Windows Forms, служб Windows, консольных приложений и приложений WPF. При запуске хостинга Asp.Net Core через консольное приложение также используется app.config.


TL; DR

Выбор файла конфигурации определяется средой хостинга, которую вы выбираете для данной услуги. Если вы используете IIS для размещения своей службы, используйте файл Web.config. Если вы используете любую другую среду размещения, используйте файл App.config. См. Настройка служб с использованием документации по файлам конфигурации, а также ознакомьтесь с Конфигурацией в ASP.NET Core.

Альпер Эбикоглу
источник
0

Там написано asp.net выше, так почему бы не сохранить настройки в базе данных и использовать специальный кеш для их получения?

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

Пример пользовательского кэша:

public enum ConfigurationSection
{
    AppSettings
}

public static class Utility
{
    #region "Common.Configuration.Configurations"

    private static Cache cache = System.Web.HttpRuntime.Cache;

    public static String GetAppSetting(String key)
    {
        return GetConfigurationValue(ConfigurationSection.AppSettings, key);
    }

    public static String GetConfigurationValue(ConfigurationSection section, String key)
    {
        Configurations config = null;

        if (!cache.TryGetItemFromCache<Configurations>(out config))
        {
            config = new Configurations();
            config.List(SNCLavalin.US.Common.Enumerations.ConfigurationSection.AppSettings);
            cache.AddToCache<Configurations>(config, DateTime.Now.AddMinutes(15));
        }

        var result = (from record in config
                      where record.Key == key
                      select record).FirstOrDefault();

        return (result == null) ? null : result.Value;
    }

    #endregion
}

namespace Common.Configuration
{
    public class Configurations : List<Configuration>
    {
        #region CONSTRUCTORS

        public Configurations() : base()
        {
            initialize();
        }
        public Configurations(int capacity) : base(capacity)
        {
            initialize();
        }
        public Configurations(IEnumerable<Configuration> collection) : base(collection)
        {
            initialize();
        }

        #endregion

        #region PROPERTIES & FIELDS

        private Crud _crud; // Db-Access layer

        #endregion

        #region EVENTS
        #endregion

        #region METHODS

        private void initialize()
        {
            _crud = new Crud(Utility.ConnectionName);
        }

        /// <summary>
        /// Lists one-to-many records.
        /// </summary>
        public Configurations List(ConfigurationSection section)
        {
            using (DbCommand dbCommand = _crud.Db.GetStoredProcCommand("spa_LIST_MyConfiguration"))
            {
                _crud.Db.AddInParameter(dbCommand, "@Section", DbType.String, section.ToString());

                _crud.List(dbCommand, PopulateFrom);
            }

            return this;
        }

        public void PopulateFrom(DataTable table)
        {
            this.Clear();

            foreach (DataRow row in table.Rows)
            {
                Configuration instance = new Configuration();
                instance.PopulateFrom(row);
                this.Add(instance);
            }
        }

        #endregion
    }

    public class Configuration
    {
        #region CONSTRUCTORS

        public Configuration()
        {
            initialize();
        }

        #endregion

        #region PROPERTIES & FIELDS

        private Crud _crud;

        public string Section { get; set; }
        public string Key { get; set; }
        public string Value { get; set; }

        #endregion

        #region EVENTS
        #endregion

        #region METHODS

        private void initialize()
        {
            _crud = new Crud(Utility.ConnectionName);
            Clear();
        }

        public void Clear()
        {
            this.Section = "";
            this.Key = "";
            this.Value = "";
        }
        public void PopulateFrom(DataRow row)
        {
            Clear();

            this.Section = row["Section"].ToString();
            this.Key = row["Key"].ToString();
            this.Value = row["Value"].ToString();
        }

        #endregion
    }
}
Узник ноль
источник