Các biến trong app.config / web.config


92

Có thể làm điều gì đó như sau trong tệp app.confighoặc web.configkhông?

<appSettings>
 <add key="MyBaseDir" value="C:\MyBase" />
 <add key="Dir1" value="[MyBaseDir]\Dir1"/>
 <add key="Dir2" value="[MyBaseDir]\Dir2"/>
</appSettings>

Sau đó, tôi muốn truy cập Dir2 trong mã của mình bằng cách chỉ cần nói:

 ConfigurationManager.AppSettings["Dir2"]

Điều này sẽ giúp ích cho tôi khi tôi cài đặt ứng dụng của mình ở các máy chủ và vị trí khác nhau, trong đó tôi sẽ chỉ phải thay đổi MỘT mục trong toàn bộ mục nhập của mình app.config. (Tôi biết tôi có thể quản lý tất cả các nối trong mã, nhưng tôi thích nó theo cách này hơn).


Tôi nghĩ anh ấy đang nói về việc xác định các biến để sử dụng trong các khóa appSettings trực tiếp trong các tệp cấu hình.
Michaël Carpentier

1
Tôi cũng đã kiểm tra bằng cách sử dụng khai báo XML <! ENTITY>, nhưng nó không được hỗ trợ do cách MS xử lý tệp web.config.
chilltemp

Cảm ơn vì những nỗ lực của bạn. Tôi không muốn sửa đổi bất kỳ mã nào. Đoạn mã đã có một câu lệnh nói rằng: string dir2 = ConfigurationManager.AppSettings ["Dir2"]. Tôi chỉ muốn dọn dẹp app.config mà bây giờ nói value = "D: \ blahdir \ dir2" thay vì value = "[MyBaseDir] \ dir2"
DeeStackOverflow

Câu trả lời:


7

Câu hỏi hay.

Tôi không nghĩ là có. Tôi tin rằng nó sẽ khá nổi tiếng nếu có một cách dễ dàng và tôi thấy rằng Microsoft đang tạo một cơ chế trong Visual Studio 2010 để triển khai các tệp cấu hình khác nhau để triển khai và thử nghiệm.

Với điều đó đã nói, tuy nhiên; Tôi nhận thấy rằng bạn trong ConnectionStringsphần này có một loại trình giữ chỗ được gọi là "| DataDirectory |". Có lẽ bạn có thể xem những gì đang làm việc ở đó ...

Đây là một đoạn từ machine.confighiển thị nó:

 <connectionStrings>
    <add
        name="LocalSqlServer"
        connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true"
        providerName="System.Data.SqlClient"
    />
 </connectionStrings>

Đó là thông tin thú vị. Có thể các biến được truy cập bằng ký hiệu ống ("|")? Hmm .. Tôi tự hỏi liệu điều này có hoạt động không: <add key = "Dir2" value = "| MyBaseDir | \ Dir2" />
DeeStackOverflow

4
Giá trị DataDirectory thực sự là một phần tử dữ liệu trong AppDomain. Bạn có thể ghi đè giá trị bằng cách sử dụng AppDomain.CurrentDomain.SetData ("DataDirectory", dataPath); Tôi đã không kiểm tra nếu bạn có thể xác định các biến số khác như thế này và nhận được chúng "autoexpanded" mặc dù ...
Peter Lillevold

22

Một giải pháp thay thế phức tạp hơn một chút nhưng linh hoạt hơn nhiều là tạo một lớp đại diện cho một phần cấu hình. Trong tệp app.config/ của bạn web.config, bạn có thể có:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!-- This section must be the first section within the <configuration> node -->
    <configSections>
        <section name="DirectoryInfo" type="MyProjectNamespace.DirectoryInfoConfigSection, MyProjectAssemblyName" />
    </configSections>

    <DirectoryInfo>
        <Directory MyBaseDir="C:\MyBase" Dir1="Dir1" Dir2="Dir2" />
    </DirectoryInfo>
</configuration>

Sau đó, trong mã .NET của bạn (tôi sẽ sử dụng C # trong ví dụ của mình), bạn có thể tạo hai lớp như sau:

using System;
using System.Configuration;

namespace MyProjectNamespace {

    public class DirectoryInfoConfigSection : ConfigurationSection {

        [ConfigurationProperty("Directory")]
        public DirectoryConfigElement Directory {
            get {
                return (DirectoryConfigElement)base["Directory"];
            }
    }

    public class DirectoryConfigElement : ConfigurationElement {

        [ConfigurationProperty("MyBaseDir")]
        public String BaseDirectory {
            get {
                return (String)base["MyBaseDir"];
            }
        }

        [ConfigurationProperty("Dir1")]
        public String Directory1 {
            get {
                return (String)base["Dir1"];
            }
        }

        [ConfigurationProperty("Dir2")]
        public String Directory2 {
            get {
                return (String)base["Dir2"];
            }
        }
        // You can make custom properties to combine your directory names.
        public String Directory1Resolved {
            get {
                return System.IO.Path.Combine(BaseDirectory, Directory1);
            }
        }
    }
}

Cuối cùng, trong mã chương trình của bạn, bạn có thể truy cập các app.configbiến của mình , sử dụng các lớp mới, theo cách sau:

DirectoryInfoConfigSection config =
  (DirectoryInfoConfigSection)ConfigurationManager.GetSection("DirectoryInfo");
String dir1Path = config.Directory.Directory1Resolved;  // This value will equal "C:\MyBase\Dir1"

1
Cảm ơn nhưng tôi đang cố gắng làm điều này mà không sửa đổi bất kỳ mã nào vì nó là một vấn đề khó khăn ở giai đoạn này.
DeeStackOverflow

Có một lỗi nhỏ trong dòng mã cuối cùng (không tính dấu ngoặc nhọn): "return System.IO.Path.Combine (MyBaseDir, Dir1);" thay vào đó phải là "return System.IO.Path.Combine (BaseDirectory, Dir1);", hoặc nếu không thì phương thức phải được đổi tên từ 'Base Directory' thành 'MyBaseDir'
TheWho

16

Bạn có thể hoàn thành việc sử dụng Thư viện mở rộng của tôi . Cũng có sẵn trên nuget ở đây .

Nó được thiết kế với điều này như một trường hợp sử dụng chính.

Ví dụ về kiểm duyệt (sử dụng AppSettings làm nguồn mặc định để mở rộng mã thông báo)

Trong app.config:

<configuration>
    <appSettings>
        <add key="Domain" value="mycompany.com"/>
        <add key="ServerName" value="db01.{Domain}"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid=uid;pwd=pwd;Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Sử dụng phương thức mở rộng .Expand () trên chuỗi được mở rộng:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

hoặc là

Sử dụng trình bao bọc Dynamic ConfigurationManager "Cấu hình" như sau (Lệnh gọi rõ ràng để Expand () không cần thiết):

var serverName = Config.AppSettings.ServerName;
// returns "db01.mycompany.com"

var connectionString = Config.ConnectionStrings.Default;
// returns "server=db01.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

Ví dụ nâng cao 1 (sử dụng AppSettings làm nguồn mặc định để mở rộng mã thông báo)

Trong app.config:

<configuration>
    <appSettings>
        <add key="Environment" value="dev"/>
        <add key="Domain" value="mycompany.com"/>
        <add key="UserId" value="uid"/>
        <add key="Password" value="pwd"/>
        <add key="ServerName" value="db01-{Environment}.{Domain}"/>
        <add key="ReportPath" value="\\{ServerName}\SomeFileShare"/>
    </appSettings>
    <connectionStrings>
        <add name="Default" connectionString="server={ServerName};uid={UserId};pwd={Password};Initial Catalog=master;" provider="System.Data.SqlClient" />
    </connectionStrings>
</configuration>

Sử dụng phương thức mở rộng .Expand () trên chuỗi được mở rộng:

var connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
connectionString.Expand() // returns "server=db01-dev.mycompany.com;uid=uid;pwd=pwd;Initial Catalog=master;"

4
Tôi nghĩ câu trả lời này được đánh giá rất thấp !!
Ahmad

Cảm ơn Ahmad! Hãy cho tôi biết bạn thích Mở rộng như thế nào.
anderly

Mặc dù đây là 'giải pháp' thời gian chạy của cài đặt ứng dụng, nó giải quyết vấn đề của tôi về việc có các cặp giá trị khóa lặp lại. Chúng tôi đã giảm đáng kể việc bảo trì cấu hình của mình bằng cách sử dụng điều này. Điều không tưởng tuyệt đối ở đây là có một plugin thời gian xây dựng để hoạt động cùng với SlowCheetah. Tôi sẽ +1 lại nếu có thể. Công cụ tuyệt vời và đặc biệt.
Ahmad

Bạn có thể vui lòng cung cấp một ví dụ ngắn gọn về cách thư viện của bạn có thể được sử dụng để thực hiện điều này không?
Ryan Gates

Đối với bất kỳ ai khác vừa mới tình cờ gặp điều này, dự án đã chết 6 năm nay, kể từ năm 2011 :(
user1003916

4

Tôi nghĩ tôi vừa nhìn thấy câu hỏi này.

Tóm lại, không, không có nội suy biến đổi trong cấu hình ứng dụng.

Bạn có hai lựa chọn

  1. Bạn có thể tự chuyển sang các biến thay thế trong thời gian chạy
  2. Tại thời điểm xây dựng, hãy xoa bóp cấu hình ứng dụng cho các chi tiết cụ thể của môi trường triển khai mục tiêu. Một số chi tiết về điều này khi đối phó với cơn ác mộng cấu hình

Đây là bài viết chính xác. Bài đăng trước của tôi (cùng câu hỏi) không hiển thị ví dụ mục nhập xml app.config. Tôi đã kiểm tra liên kết của bạn - nó quá nhiều công việc và không muốn dành thời gian ở đó. Chúng tôi có app.configs riêng cho các hộp khác nhau và tôi muốn loại bỏ điều đó.
DeeStackOverflow

3

Bạn có một cặp đôi tùy chọn. Bạn có thể làm điều này với bước xây dựng / triển khai sẽ xử lý tệp cấu hình của bạn thay thế các biến của bạn bằng giá trị chính xác.

Một tùy chọn khác sẽ là xác định phần Cấu hình của riêng bạn hỗ trợ điều này. Ví dụ, hãy tưởng tượng xml này:

<variableAppSettings>
 <variables>
    <add key="@BaseDir" value="c:\Programs\Widget"/>
 </variables>
 <appSettings>
    <add key="PathToDir" value="@BaseDir\Dir1"/>
 </appSettings>
</variableAppSettings>

Bây giờ bạn sẽ thực hiện điều này bằng cách sử dụng các đối tượng cấu hình tùy chỉnh sẽ xử lý việc thay thế các biến cho bạn trong thời gian chạy.


Tôi không thấy xml của bạn trong bài đăng (thụt lề dòng 5 ký tự để có thể đăng thẻ xml - tôi đã gặp vấn đề tương tự lần trước). Ngoài ra, 'đối tượng cấu hình tùy chỉnh' là gì? Tôi thích không mã hóa hơn để đạt được điều này vì những thay đổi mã hóa ở giai đoạn này sẽ khiến chúng ta phải lùi lại rất nhiều.
DeeStackOverflow

Cấu hình tùy chỉnh chắc chắn liên quan đến mã hóa [đơn giản]. Nhưng IMHO nó luôn là lựa chọn tốt nhất của bạn. Tôi hầu như không bao giờ sử dụng appSettings, thay vào đó tôi thích tạo một cấu hình tùy chỉnh cho mọi dự án.
Portman

3

Thông thường, tôi kết thúc việc viết một lớp tĩnh với các thuộc tính để truy cập từng cài đặt của web.config của tôi.

public static class ConfigManager 
{
    public static string MyBaseDir
    {
        return ConfigurationManager.AppSettings["MyBaseDir"].toString();
    }

    public static string Dir1
    {
        return MyBaseDir + ConfigurationManager.AppSettings["Dir1"].toString();
    }

}

Thông thường, tôi cũng thực hiện chuyển đổi kiểu khi được yêu cầu trong lớp này. Nó cho phép có quyền truy cập đã nhập vào cấu hình của bạn và nếu cài đặt thay đổi, bạn chỉ có thể chỉnh sửa chúng ở một nơi.

Thông thường, việc thay thế các cài đặt bằng lớp này tương đối dễ dàng và mang lại khả năng bảo trì cao hơn nhiều.


3

Bạn có thể sử dụng các biến môi trường trong của bạn app.configcho tình huống mà bạn mô tả

<configuration>
  <appSettings>
    <add key="Dir1" value="%MyBaseDir%\Dir1"/>
  </appSettings>
</configuration>

Sau đó, bạn có thể dễ dàng nhận được đường dẫn với:

var pathFromConfig = ConfigurationManager.AppSettings["Dir1"];
var expandedPath = Environment.ExpandEnvironmentVariables(pathFromConfig);

2

Bên trong, <appSettings>bạn có thể tạo các khóa ứng dụng,

<add key="KeyName" value="Keyvalue"/>

Sau này, bạn có thể truy cập các giá trị này bằng cách sử dụng:

ConfigurationManager.AppSettings["Keyname"]

Để sử dụng lớp ConfigurationManager, bạn cần thêm tham chiếu đến System.Configuration và thêm một câu lệnh using cho System.Configuration (nhập trong VB)
cjk

2
Dấu hiệu này đúng nhưng không phải là câu trả lời cho câu hỏi được hỏi.
Michaël Carpentier

1

Tôi sẽ đề nghị bạn DslConfig . Với DslConfig, bạn có thể sử dụng các tệp cấu hình phân cấp từ Cấu hình chung, Cấu hình cho mỗi máy chủ lưu trữ để cấu hình cho mỗi ứng dụng trên mỗi máy chủ lưu trữ (xem AppSpike).
Nếu điều này phức tạp đối với bạn, bạn chỉ có thể sử dụng cấu hình chung Variables.var
Chỉ cần cấu hình trong Varibales.var

baseDir = "C:\MyBase"
Var["MyBaseDir"] = baseDir
Var["Dir1"] = baseDir + "\Dir1"
Var["Dir2"] = baseDir + "\Dir2"

Và nhận các giá trị cấu hình với

Configuration config = new DslConfig.BooDslConfiguration()
config.GetVariable<string>("MyBaseDir")
config.GetVariable<string>("Dir1")
config.GetVariable<string>("Dir2")

0

Tôi không nghĩ rằng bạn có thể khai báo và sử dụng các biến để xác định các khóa appSettings trong một tệp cấu hình. Tôi đã luôn quản lý các nối trong mã giống như bạn.


0

Tôi đang đấu tranh một chút với những gì bạn muốn, nhưng bạn có thể thêm tệp ghi đè vào cài đặt ứng dụng, sau đó đặt tệp ghi đè đó trên cơ sở từng môi trường.

<appSettings file="..\OverrideSettings.config">

0

Để triển khai các sản phẩm mà chúng tôi cần định cấu hình nhiều mục có giá trị tương tự, chúng tôi sử dụng các ứng dụng bảng điều khiển nhỏ có chức năng đọc XML và cập nhật dựa trên các tham số được truyền vào. Sau đó, trình cài đặt gọi chúng sau khi yêu cầu người dùng cung cấp Thông tin bắt buộc.


0

Tôi khuyên bạn nên làm theo giải pháp của Matt Hamsmith. Nếu đó là một vấn đề để triển khai, thì tại sao không tạo một phương thức mở rộng để triển khai điều này trong nền trên lớp AppSettings?

Cái gì đó như:

    public static string GetValue(this NameValueCollection settings, string key)
    {

    }

Bên trong phương thức, bạn tìm kiếm thông qua DictionaryInfoConfigSection bằng cách sử dụng Linq và trả về giá trị bằng khóa phù hợp. Tuy nhiên, bạn sẽ cần cập nhật tệp cấu hình thành một cái gì đó dọc theo những dòng sau:

<appSettings>
  <DirectoryMappings>
    <DirectoryMap key="MyBaseDir" value="C:\MyBase" />
    <DirectoryMap key="Dir1" value="[MyBaseDir]\Dir1"/>
    <DirectoryMap key="Dir2" value="[MyBaseDir]\Dir2"/>
  </DirectoryMappings>
</appSettings>

0

Tôi đã đưa ra giải pháp này:

  1. Trong ứng dụng Settings.settings, tôi đã xác định một ConfigurationBase biến (với type = string Scope = Application)
  2. Tôi đã giới thiệu một biến trong các thuộc tính mục tiêu trong Settings.settings, tất cả các thuộc tính đó phải được đặt thành Phạm vi = Người dùng
  3. Trong app.xaml.cs, tôi đọc ra giá trị nếu ConfigurationBase
  4. Trong app.xaml.cs, tôi đã thay thế tất cả các biến bằng giá trị ConfigurationBase. Để thay thế các giá trị tại thời điểm chạy, các thuộc tính phải được đặt thành Scopr = Người dùng

Tôi không thực sự hài lòng với giải pháp này vì tôi phải thay đổi tất cả các thuộc tính theo cách thủ công, nếu tôi thêm một thuộc tính mới, tôi phải xem nó trong app.xaml.cs.

Đây là đoạn mã từ App.xaml.cs:

string configBase = Settings.Default.ConfigurationBase;
Settings.Default.CommonOutput_Directory = Settings.Default.CommonOutput_Directory.Replace("${ConfigurationBase}", configBase);

CẬP NHẬT

Vừa tìm thấy một cải tiến (lại là một đoạn mã từ app.xaml.cs):

string configBase = Settings.Default.ConfigurationBase;

foreach (SettingsProperty settingsProperty in Settings.Default.Properties)
{
    if (!settingsProperty.IsReadOnly && settings.Default[settingsProperty.Name] is string)
    {
        Settings.Default[settingsProperty.Name] = ((string)Settings.Default[settingsProperty.Name]).Replace("${ConfigurationBase}", configBase);
    }
}

Bây giờ các thay thế hoạt động cho tất cả các thuộc tính trong cài đặt của tôi có Type = string và Scope = User. Tôi nghĩ tôi thích nó theo cách này.

CẬP NHẬT2

Rõ ràng thiết lập Phạm vi = Ứng dụng không bắt buộc khi chạy trên các thuộc tính.


0

Ba giải pháp khả thi

Tôi biết mình đến bữa tiệc muộn, tôi đang tìm xem có giải pháp mới nào cho vấn đề cài đặt cấu hình biến không. Có một số câu trả lời liên quan đến các giải pháp tôi đã sử dụng trong quá khứ nhưng hầu hết có vẻ hơi phức tạp. Tôi nghĩ rằng tôi sẽ xem xét các giải pháp cũ của mình và kết hợp các triển khai lại với nhau để nó có thể giúp những người đang gặp khó khăn với cùng một vấn đề.

Đối với ví dụ này, tôi đã sử dụng cài đặt ứng dụng sau trong ứng dụng bảng điều khiển:

<appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>

1. Sử dụng các biến môi trường

Tôi tin rằng câu trả lời của autocro autocro đã chạm vào nó. Tôi chỉ đang thực hiện một triển khai đủ khi xây dựng hoặc gỡ lỗi mà không cần phải đóng studio trực quan. Tôi đã sử dụng giải pháp này trong ngày ...

  • Tạo một sự kiện trước khi xây dựng sẽ sử dụng các biến MSBuild

    Cảnh báo: Sử dụng một biến sẽ không được thay thế dễ dàng, vì vậy hãy sử dụng tên dự án của bạn hoặc một cái gì đó tương tự làm tên biến.

    SETX BaseDir "$(ProjectDir)"

  • Đặt lại các biến; bằng cách sử dụng một cái gì đó như sau:

    Làm mới các biến môi trường trên Stack Overflow

  • Sử dụng cài đặt trong mã của bạn:

'

private void Test_Environment_Variables()
{
    string BaseDir = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
    string ExpandedPath = Environment.ExpandEnvironmentVariables(BaseDir).Replace("\"", ""); //The function addes a " at the end of the variable
    Console.WriteLine($"From within the C# Console Application {ExpandedPath}");
}

'

2. Sử dụng nội suy chuỗi:

  • Sử dụng hàm string.Format ()

`

private void Test_Interpollation()
{
    string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
    string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
    string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
    Console.WriteLine($"Using old interpollation {ExpandedPath}");
}

`

3. Sử dụng một lớp tĩnh, Đây là giải pháp mà tôi chủ yếu sử dụng.

  • Việc thực hiện

`

private void Test_Static_Class()
{
    Console.WriteLine($"Using a static config class {Configuration.BinPath}");
}

`

  • Lớp tĩnh

`

static class Configuration
{
    public static string BinPath
    {
        get
        {
            string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            return SolutionPath + ConfigPath;
        }
    }
}

`

Mã số dự án:

App.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
    </startup>
  <appSettings>
    <add key="EnvironmentVariableExample" value="%BaseDir%\bin"/>
    <add key="StaticClassExample" value="bin"/>
    <add key="InterpollationExample" value="{0}bin"/>
  </appSettings>
</configuration>

Program.cs

using System;
using System.Configuration;
using System.IO;

namespace ConfigInterpollation
{
    class Program
    {
        static void Main(string[] args)
        {
            new Console_Tests().Run_Tests();
            Console.WriteLine("Press enter to exit");
            Console.ReadLine();
        }        
    }

    internal class Console_Tests
    {
        public void Run_Tests()
        {
            Test_Environment_Variables();
            Test_Interpollation();
            Test_Static_Class();
        }
        private void Test_Environment_Variables()
        {
            string ConfigPath = ConfigurationManager.AppSettings["EnvironmentVariableExample"];
            string ExpandedPath = Environment.ExpandEnvironmentVariables(ConfigPath).Replace("\"", "");
            Console.WriteLine($"Using environment variables {ExpandedPath}");
        }

        private void Test_Interpollation()
        {
            string ConfigPath = ConfigurationManager.AppSettings["InterpollationExample"];
            string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
            string ExpandedPath = string.Format(ConfigPath, SolutionPath.ToString());
            Console.WriteLine($"Using interpollation {ExpandedPath}");
        }

        private void Test_Static_Class()
        {
            Console.WriteLine($"Using a static config class {Configuration.BinPath}");
        }
    }

    static class Configuration
    {
        public static string BinPath
        {
            get
            {
                string ConfigPath = ConfigurationManager.AppSettings["StaticClassExample"];
                string SolutionPath = Path.GetFullPath(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"..\..\"));
                return SolutionPath + ConfigPath;
            }
        }
    }
}

Sự kiện xây dựng trước:

Cài đặt dự án -> Xây dựng sự kiện

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.