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


162

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

Чи існує кращий / рекомендований / бажаний метод для досягнення подібного ефекту? Або однаково, я підійшов до цього зовсім неправильно і чи є кращий підхід?


Я хочу відключити налагодження в 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 файл AND жорстко ввів у код значення за замовчуванням. Навіть коли я нарешті з’ясував (і вилучив) файл налаштувань, це значення за замовчуванням залишилось у жорсткому коді та вбудовувалось у exe. ДУЖЕ ФУСТРАЦІЯ !! Завдяки цій публікації тепер я можу позбутися тієї "особливості"
Майк К

+1 Ця відповідь є критичною : Якщо ви хочете, щоб ваш параметр зайшов у файл app.config, встановіть для його атрибута GenerateDefaultValueInCode значення False (за замовчуванням - True).
Сабунку

34

Тут пов'язане запитання:

Покращення процесу складання

Налаштування файлів постачається із можливістю зміни параметрів:

<appSettings file="Local.config">

Замість того, щоб перевіряти два файли (або більше), ви перевіряєте лише конфігураційний файл за замовчуванням, а потім на кожній цільовій машині ви ставите Local.config, лише розділ програми Налаштування, який має зміни для цієї конкретної машини.

Якщо ви використовуєте конфігураційні розділи, еквівалент:

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

З цим ви можете запропонувати йому оновити / змінити розділи вашого файлу web.config залежно від конфігурації збірки.

Подивіться на цей запис у блозі від Скотта Гу для швидкого огляду / зразка.


8

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

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

У будь-якому випадку ваші конфігураційні файли можуть мати будь-які значення розробника, які ви хочете, і вони будуть добре працювати у вашому середовищі розробників, не порушуючи виробничих систем. Ми виявили, що розробники мають меншу ймовірність довільно змінювати змінні сценарію збірки під час тестування речей, тому випадкові неправильні конфігурації виявляються рідше, ніж інші методи, які ми намагалися, хоча все-таки потрібно додавати кожен var на початку процесу, щоб значення dev за замовчуванням не підштовхується до prod.


7

Мій поточний роботодавець вирішив це питання, поклавши перший у програму machine.config рівень dev (налагодження, етап, живий т. Д.). Потім вони написали код, щоб забрати це і скористатися правильним файлом конфігурації. Це вирішило проблему з неправильним рядком з'єднання після розгортання програми.

Вони нещодавно написали центральну веб-службу, яка надсилає назад правильну рядок з'єднання зі значення у значенні machine.config.

Це найкраще рішення? Напевно, ні, але це працює на них.


1
Насправді я думаю, що це досить проклято елегантно, оскільки я люблю тримати різні версії конфігурації видимими в рішенні, навіть якщо вони не є в прямому ефірі.
annakata

1
Це дуже інтригуюче рішення. Хочеться переглянути приклад цього в дії.
Майк К

5

Одним із рішень, які мені добре працювали, було використання WebDeploymentProject. У мене було 2/3 різних файлів web.config на своєму веб-сайті, і при публікації, залежно від обраного режиму конфігурації (випуск / постановка / тощо), я б скопіював через Web.Release.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. Він включає завдання "XmlMassUpdate", яке може "масово оновити" записи у будь-якому XML-файлі, як тільки ви дасте йому правильний вузол для початку.

Впровадити:

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

2) Потрібно мати файл "Substitutions.xml", який містить лише записи, які є РІЗНИМИ (в основному програми та налаштування 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 рівних елементів). Для розгортання з натисканням клавіші в кожну я змінюю 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 Services, Console Apps і 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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.