Cấu hình .NET (app.config / web.config / settings.sinstall)


162

Tôi có một ứng dụng .NET có các tệp cấu hình khác nhau cho các bản dựng Gỡ lỗi và Phát hành. Ví dụ: tệp app.config gỡ lỗi trỏ đến Máy chủ SQL phát triển đã bật gỡ lỗi và mục tiêu phát hành trỏ đến Máy chủ SQL trực tiếp. Ngoài ra còn có các cài đặt khác, một số cài đặt khác nhau trong gỡ lỗi / phát hành.

Tôi hiện đang sử dụng hai tệp cấu hình riêng biệt (debug.app.config và release.app.config). Tôi có một sự kiện xây dựng trong dự án cho biết nếu đây là bản dựng phát hành thì sao chép release.app.config sang app.config, nếu không thì sao chép debug.app.config sang app.config.

Vấn đề là ứng dụng dường như nhận được các cài đặt từ tệp settings.s settings, vì vậy tôi phải mở cài đặt. Cài đặt trong Visual Studio, sau đó nhắc tôi rằng các cài đặt đã thay đổi để tôi chấp nhận các thay đổi, lưu cài đặt. để xây dựng lại để làm cho nó sử dụng các cài đặt chính xác.

Có một phương pháp tốt hơn / được đề nghị / ưa thích để đạt được hiệu quả tương tự không? Hoặc tương tự, tôi đã tiếp cận điều này hoàn toàn sai và có cách tiếp cận tốt hơn?


Tôi muốn vô hiệu hóa gỡ lỗi trong windows, tôi đã thử bằng cách bỏ chọn tất cả các hộp kiểm trong cài đặt gỡ lỗi, nhưng tôi vẫn có thể gỡ lỗi bản phát hành bin exe .. Bất cứ ai giúp tôi về điều này ..
Vinoth Narayan

Câu trả lời:


62

Bất kỳ cấu hình nào có thể khác nhau giữa các môi trường nên được lưu trữ ở cấp độ máy , không phải ở cấp ứng dụng . (Thông tin thêm về các mức cấu hình.)

Đây là các loại yếu tố cấu hình mà tôi thường lưu trữ ở cấp độ máy:

Khi mỗi môi trường (nhà phát triển, tích hợp, kiểm tra, giai đoạn, trực tiếp) có các cài đặt duy nhất của riêng nó trong thư mục c: \ Windows \ Microsoft.NET \ Framework64 \ v2.0.50727 \ CONFIG , thì bạn có thể quảng bá mã ứng dụng của mình giữa các môi trường mà không cần bất kỳ sửa đổi sau xây dựng.

Và rõ ràng, nội dung của thư mục CONFIG cấp độ máy được kiểm soát phiên bản trong một kho lưu trữ khác hoặc cấu trúc thư mục khác với ứng dụng của bạn. Bạn có thể làm cho các tệp .config của mình thân thiện hơn với kiểm soát nguồn thông qua việc sử dụng configSource thông minh .

Tôi đã làm điều này trong 7 năm, trên hơn 200 ứng dụng ASP.NET tại hơn 25 công ty khác nhau. (Không cố gắng khoe khoang, chỉ muốn cho bạn biết rằng tôi chưa bao giờ thấy tình huống mà phương pháp này không hiệu quả.)


3
Còn tình huống bạn không kiểm soát máy chủ web và do đó không thể thay đổi cấu hình cấp máy thì sao? Ví dụ bao gồm máy chủ web của bên thứ 3 hoặc máy chủ web được chia sẻ giữa nhiều phòng ban trong doanh nghiệp.
RationalGeek

1
Sẽ không làm việc. Nhưng trong kỷ nguyên của máy ảo, Amazon EC2 và 400 máy chủ của Dell, có ai thực sự làm gì nghiêm trọng trên các máy dùng chung không? Không cố gắng tỏ ra nhẫn tâm - Tôi thực sự nghĩ rằng nếu bạn đang làm việc trên một máy chủ web được chia sẻ, bạn nên đánh giá lại.
Portman

7
Hầu hết các tập đoàn tôi đã làm việc với các trang web nội bộ đều lưu trữ nhiều ứng dụng trên một máy chủ - việc đánh giá lại sẽ phải được thực hiện ở cấp độ công ty
MPritchard

Nhiều ứng dụng trên một máy chủ đều ổn miễn là các ứng dụng đều nằm trong cùng một "môi trường". Tức là, bạn sẽ không muốn phiên bản TRỰC TIẾP của App1 trên cùng một máy chủ với phiên bản DEV của App2. Ví dụ: cài đặt SMTP của bạn sẽ được chia sẻ trên tất cả các ứng dụng của bạn. Trong sản xuất, bạn trỏ đến một máy chủ thư thực sự; trong quá trình phát triển, bạn trỏ đến một tệp trên đĩa.
Portman

7
Tôi biết rằng điều này sẽ hoạt động, nhưng điều này vẫn đi ngược lại những gì tôi muốn giới thiệu khi cố gắng tự động hóa việc triển khai. Tôi nghĩ rằng các cài đặt là dành riêng cho ứng dụng, chúng cần phải được kiểm soát phiên bản cùng với ứng dụng và phát triển cùng với nó. Dựa vào cấu hình Máy chỉ thay đổi, theo ý kiến ​​của tôi làm cho nó khó hơn. Tôi thích giữ những thứ thay đổi cùng nhau và triển khai chúng cùng nhau. Nếu tôi thêm cài đặt mới cho Dev, tôi có thể cần một cài đặt tương đương cho sản phẩm.
Miguel Madero

51

Điều này có thể giúp một số người giao dịch với Settings.sinstall và App.config: Coi chừng thuộc tính GenerateDefaultValueInCode trong ngăn Thuộc tính trong khi chỉnh sửa bất kỳ giá trị nào trong lưới Settings.sinstall trong Visual Studio (trong trường hợp của tôi là Visual Studio 2008).

Nếu bạn đặt GenerateDefaultValueInCode thành True (True là mặc định ở đây!), Giá trị mặc định được biên dịch vào EXE (hoặc DLL), bạn có thể tìm thấy nó được nhúng trong tệp khi bạn mở nó trong trình soạn thảo văn bản thuần túy.

Tôi đã làm việc trên một ứng dụng giao diện điều khiển và nếu tôi đã mặc định trong EXE, ứng dụng này luôn bỏ qua tệp cấu hình được đặt trong cùng thư mục! Khá là một cơn ác mộng và không có thông tin về điều này trên toàn bộ Internet.


7
Đây chính xác là những gì đã xảy ra với tôi vào cuối tuần qua. Tôi đã nhổ rất nhiều tóc cố gắng tìm ra lý do tại sao ứng dụng của tôi dường như bỏ qua tệp app.config của tôi! Nó được cho là kết nối với một dịch vụ web và url dịch vụ nằm trong app.config của tôi. Không biết với tôi, khi tôi tạo tham chiếu web, nó cũng tạo tệp Cài đặt. Cài đặt VÀ mã hóa giá trị mặc định vào mã. Ngay cả khi cuối cùng tôi đã tìm ra (và loại bỏ) tệp cài đặt, giá trị mặc định đó vẫn nằm trong mã cứng và được nhúng vào exe. RẤT BỰC BỘI!! Nhờ vào bài đăng này, bây giờ tôi có thể thoát khỏi "tính năng" đó
Mike K

+1 Câu trả lời này là câu trả lời quan trọng : Nếu bạn muốn cài đặt của mình đi vào tệp app.config, hãy đặt thuộc tính GenerateDefaultValueInCode thành Sai (mặc định là True).
Sabuncu

34

Có một câu hỏi liên quan ở đây:

Cải thiện quy trình xây dựng của bạn

Các tập tin cấu hình đi kèm với một cách để ghi đè cài đặt:

<appSettings file="Local.config">

Thay vì kiểm tra hai tệp (hoặc nhiều hơn), bạn chỉ kiểm tra tệp cấu hình mặc định, rồi trên mỗi máy đích, bạn đặt Local.config, chỉ với phần appSinstall có phần ghi đè cho máy cụ thể đó.

Nếu bạn đang sử dụng các phần cấu hình, tương đương là:

configSource="Local.config"

Tất nhiên, nên tạo bản sao dự phòng của tất cả các tệp Local.config từ các máy khác và kiểm tra chúng ở đâu đó, nhưng không phải là một phần của các giải pháp thực tế. Mỗi nhà phát triển đặt "bỏ qua" trên tệp Local.config để nó không bị kiểm tra, điều này sẽ ghi đè lên tệp của mọi người khác.

(Bạn thực sự không phải gọi nó là "Local.config", đó chỉ là những gì tôi sử dụng)


14

Từ những gì tôi đang đọc, có vẻ như bạn đang sử dụng Visual Studio cho quá trình xây dựng của mình. Bạn đã nghĩ về việc sử dụng MSBuild và Nant thay thế?

Cú pháp xml của Nant hơi kỳ lạ nhưng một khi bạn hiểu nó, việc làm những gì bạn đề cập trở nên khá tầm thường.

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


8

Chúng tôi đã từng sử dụng các dự án Triển khai Web nhưng từ đó đã chuyển sang NAnt. Thay vì phân nhánh và sao chép các tệp cài đặt khác nhau, chúng tôi hiện đang nhúng các giá trị cấu hình trực tiếp vào tập lệnh xây dựng và đưa chúng vào các tệp cấu hình của chúng tôi thông qua các tác vụ xmlpoke:

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

Trong cả hai trường hợp, các tệp cấu hình của bạn có thể có bất kỳ giá trị nào dành cho nhà phát triển mà bạn muốn và chúng sẽ hoạt động tốt từ bên trong môi trường dev của bạn mà không phá vỡ hệ thống sản xuất của bạn. Chúng tôi đã phát hiện ra rằng các nhà phát triển ít có khả năng thay đổi tùy ý các biến tập lệnh xây dựng khi kiểm tra mọi thứ, do đó, các cấu hình sai ngẫu nhiên hiếm hơn so với các kỹ thuật khác mà chúng tôi đã thử, mặc dù vậy vẫn cần thêm từng var trong quá trình để có giá trị dev không được đẩy lên prod theo mặc định.


7

Chủ nhân hiện tại của tôi đã giải quyết vấn đề này bằng cách trước tiên đặt cấp độ dev (gỡ lỗi, giai đoạn, trực tiếp, v.v.) trong tệp machine.config. Sau đó, họ đã viết mã để chọn nó và sử dụng đúng tệp cấu hình. Điều đó đã giải quyết vấn đề với chuỗi kết nối sai sau khi ứng dụng được triển khai.

Họ vừa mới viết một dịch vụ web trung tâm gửi lại chuỗi kết nối chính xác từ giá trị trong giá trị machine.config.

Đây có phải là giải pháp tốt nhất? Có lẽ là không, nhưng nó làm việc cho họ.


1
Trên thực tế tôi nghĩ rằng điều đó khá là tao nhã, vì tôi muốn giữ các phiên bản cấu hình khác nhau hiển thị trong một giải pháp, ngay cả khi chúng không tồn tại.
annakata

1
Đây là một giải pháp rất hấp dẫn. Rất thích xem qua một ví dụ về điều này trong hành động.
Mike K

5

Một trong những giải pháp giúp tôi làm việc tốt là sử dụng WebDeploymentProject. Tôi đã có 2/3 tệp web.config khác nhau trong trang web của mình và khi xuất bản, tùy thuộc vào chế độ cấu hình đã chọn (phát hành / dàn dựng / v.v ...) tôi sẽ sao chép qua Web.Release.config và đổi tên nó thành web. cấu hình trong sự kiện AfterBuild và xóa những cái tôi không cần (ví dụ 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

Dự án của chúng tôi có cùng một vấn đề khi chúng tôi phải duy trì cấu hình cho dev, qa, uat và prod. Đây là những gì chúng tôi đã làm theo (chỉ áp dụng nếu bạn quen thuộc với MSBuild):

Sử dụng MSBuild với phần mở rộng nhiệm vụ Cộng đồng MSBuild. Nó bao gồm tác vụ 'XmlMassUpdate' có thể 'cập nhật hàng loạt' trong bất kỳ tệp XML nào khi bạn cung cấp cho nó nút chính xác để bắt đầu.

Thực hiện:

1) Bạn cần có một tệp cấu hình sẽ có các mục dev env của bạn; đây là tập tin cấu hình trong giải pháp của bạn

2) Bạn cần có tệp 'Substitutions.xml', chỉ chứa các mục nhập KHÁC BIỆT (phần lớn là cài đặt ứng dụng và ConnectionStrings) cho mỗi môi trường. Các mục không thay đổi trên môi trường không cần phải được đặt trong tệp này. Họ có thể sống trong tệp web.config của giải pháp và sẽ không bị tác vụ chạm vào

3) Trong tệp xây dựng của bạn, chỉ cần gọi tác vụ cập nhật hàng loạt XML và cung cấp môi trường phù hợp làm tham số.

Xem ví dụ dưới đây:

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

thay thế '$ Môi trường' bằng 'QA' hoặc 'Prod' dựa trên những gì env. bạn đang xây dựng cho. Lưu ý rằng bạn nên làm việc trên một bản sao của tệp cấu hình chứ không phải chính tệp cấu hình thực tế để tránh mọi lỗi không thể phục hồi.

Chỉ cần chạy tệp xây dựng và sau đó di chuyển tệp cấu hình được cập nhật vào môi trường triển khai của bạn và bạn đã hoàn tất!

Để biết tổng quan tốt hơn, hãy đọc điều này:

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


2

Giống như bạn, tôi cũng đã thiết lập 'multi' app.config - ví dụ: app.configDEV, app.configTEST, app.config.LOCAL. Tôi thấy một số lựa chọn thay thế tuyệt vời được đề xuất, nhưng nếu bạn thích cách nó phù hợp với bạn, tôi sẽ thêm vào như sau:

Tôi có một
<appSettings>
<add key = "Env" value = "[Local] "/> ứng dụng cho mỗi ứng dụng Tôi thêm ứng dụng này vào giao diện người dùng trong thanh tiêu đề: từ Cấu hình Trình quản lý.AppSinstall.Get ("Env");

Tôi chỉ đổi tên cấu hình thành cái tôi đang nhắm mục tiêu (Tôi có một dự án với 8 ứng dụng có nhiều cấu hình cơ sở dữ liệu / wcf so với 4 số chẵn). Để triển khai với clickonce vào mỗi tôi thay đổi 4 lần trong dự án và đi. (điều này tôi muốn tự động hóa)

Gotcha duy nhất của tôi là nhớ 'dọn dẹp tất cả' sau khi thay đổi, vì cấu hình cũ bị 'kẹt' sau khi đổi tên thủ công. (Mà tôi nghĩ SILL sẽ khắc phục sự cố thiết lập của bạn).

Tôi thấy điều này hoạt động rất tốt (một ngày nào đó tôi sẽ có thời gian để xem MSBuild / NAnt)


0

Web.config:

Web.config là cần thiết khi bạn muốn lưu trữ ứng dụng của mình trên IIS. Web.config là một tệp cấu hình bắt buộc cho IIS để định cấu hình cách nó sẽ hoạt động như một proxy ngược trước Kestrel. Bạn phải duy trì một web.config nếu bạn muốn lưu trữ nó trên IIS.

AppSetting.json:

Đối với mọi thứ khác không liên quan đến IIS, bạn sử dụng AppSetting.json. AppSetting.json được sử dụng cho lưu trữ Asp.Net Core. ASP.NET Core sử dụng biến môi trường "ASPNETCORE_ENVIRONMENT" để xác định môi trường hiện tại. Theo mặc định, nếu bạn chạy ứng dụng của mình mà không đặt giá trị này, nó sẽ tự động mặc định cho môi trường Sản xuất và sử dụng tệp "AppSetting.production.json". Khi bạn gỡ lỗi thông qua Visual Studio, nó sẽ đặt môi trường thành Phát triển để nó sử dụng "AppSetting.json". Xem trang web này để hiểu cách đặt biến môi trường lưu trữ trên Windows.

Ứng dụng.config:

App.config là một tệp cấu hình khác được .NET sử dụng, chủ yếu được sử dụng cho Windows Forms, Windows Services, Console Apps và các ứng dụng WPF. Khi bạn khởi động lưu trữ Asp.Net Core thông qua ứng dụng bảng điều khiển, app.config cũng được sử dụng.


TL; DR

Sự lựa chọn của tệp cấu hình được xác định bởi môi trường lưu trữ bạn chọn cho dịch vụ. Nếu bạn đang sử dụng IIS để lưu trữ dịch vụ của mình, hãy sử dụng tệp Web.config. Nếu bạn đang sử dụng bất kỳ môi trường lưu trữ nào khác, hãy sử dụng tệp App.config. Xem cấu hình dịch vụ bằng tài liệu tệp cấu hình và cũng xem cấu hình trong ASP.NET Core.


0

Nó nói asp.net ở trên, vậy tại sao không lưu cài đặt của bạn trong cơ sở dữ liệu và sử dụng bộ đệm tùy chỉnh để truy xuất chúng?

Lý do chúng tôi đã làm điều đó bởi vì nó dễ dàng hơn (đối với chúng tôi) để cập nhật cơ sở dữ liệu liên tục hơn là để có được quyền cập nhật liên tục các tệp sản xuất.

Ví dụ về Cache tùy chỉnh:

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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.