[ASP.NET] 動態變更 SqlDataSource 中使用的 Web.config 裡的 Connection 字串

在我目前的工作中, 環境略為有點複雜, 牽涉到開發、測試和正式環境, 而使用的資料庫伺服器也有兩到三個。如果能夠動態地切換不同環境下所使用的資料庫連線字串, 一定對工作相當有幫助。當然, 如果我都寫 ADO.NET 指令去指定連線字串, 我自然可以動態地決定應該採用哪一個連線字串。但是, 我又不想放棄簡單易用的 GridView/FormView 搭配 SqlDataSource 的作業方式。無可誨言的, 對於那些許多很小型的資料表、很少的資料、很簡單的工作, 實在沒有什麼能比上述情境更方便處理了...

在我目前的工作中, 環境略為有點複雜, 牽涉到開發、測試和正式環境, 而使用的資料庫伺服器也有兩到三個。如果能夠動態地切換不同環境下所使用的資料庫連線字串, 一定對工作相當有幫助。

當然, 如果我都寫 ADO.NET 指令去指定連線字串, 我自然可以動態地決定應該採用哪一個連線字串。但是, 我又不想放棄簡單易用的 GridView/FormView 搭配 SqlDataSource 的作業方式。無可誨言的, 對於那些許多很小型的資料表、很少的資料、很簡單的工作, 實在沒有什麼能比上述情境更方便處理了!

對於 SqlDataSource 裡那道 ConnectionString="<%$ ConnectionStrings:ConnectionString1 %>" 指令, 絕大部份初學者都會認為就是這樣了。你除了指定寫在 Web.config 中的既有連線字串外, 你沒辦法自己任意指定它去讀取某個字串, 或者某個物件的屬性。如果你去查閱官方文件, 你也看不到任何其它相關且有用的資訊。

但是, 如果情況可以改變呢?

例如, 假設我寫了一個 SqlHelper 類別, 並且加入一個 MyConnString 屬性; 這個屬性可以視實體網站是哪一個而決定採用哪一個寫在 Web.config 裡的 ConnectionString, 那麼, 使用如下的寫法, 就可以讓 SqlDataSource 不再堅持只能讀取 Web.config 中的 ConnectionString 了:

<asp:SqlDataSource ConnectionString="<%$ SQL: Johnny.SqlHelper.MyConnString %>" ...

如上, "SQL" 是我自訂的一個前綴字串 (Prefix), 後面的 Johnny.SqlHelper.MyConnString 則是我的一個還輯物件。基本上, 你不一定要採用和我一樣的做法, 你可以使用更多、有變化的寫法; 關於這點, 我有空時可以再補充。為求簡捷, 我目前只介紹我所採用的寫法。

要達到上述功能, 我們必須先實作 ExpressionPrefix 這個 attribute, 而且要覆寫 GetCodeExpression 這個方法, 讓它回傳一個 CodeExpression 物件:


using System.Web.Compilation;
using System.CodeDom;
using System.Web.UI;
...
[ExpressionPrefix("SQL")]
public class SqlHelper : System.Web.Compilation.ExpressionBuilder
{
    private static string testConnStringName = "MyConnectionString1";
    private static string productionConnStringName = "MyConnectionString2";

    public static string currentConnectionString
    {
        get
        {
            return (IsProductionServer) ?
                productionConnStringName : testConnStringName;
        }
    }

    public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)
    {
        return new CodeSnippetExpression(entry.Expression);
    }

    public static string BoConnString
    {
        get
        {
            Configuration rootWebConfig =
                (HttpContext.Current != null) ?
                    WebConfigurationManager.OpenWebConfiguration(HostingEnvironment.ApplicationVirtualPath) :
                    ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
            System.Configuration.ConnectionStringSettings connString;
            if (0 < rootWebConfig.ConnectionStrings.ConnectionStrings.Count)
            {
                connString =
                    rootWebConfig.ConnectionStrings.ConnectionStrings[currentConnectionString];
                return connString.ToString();
            }
            else
                return null;
        }
    }

    public static bool IsProductionServer
    {
        get
        {
            return System.Environment.MachineName == "JOHNNY_PC"; // 自行撰寫程式決定
        }
    }
}

在上述程式中, IsProductionServer 這個屬性是用來判斷網站運行的主機是否為 Production Server。你可以簡單地把上述程式中的 "JOHNNY_PC" 已成你的正式主機的名稱即可。

上述程式的重點在於實作 ExpressionPrefix 並覆寫 GetCodeExpression 兩部份。其它幾行程式完全只是我的業務邏輯, 你不一定要寫得跟它一樣。

此外, 要記得在 Web.config 中加入如下的設定:


    <compilation debug="true" targetFramework="4.5">
      <expressionBuilders>
        <add expressionPrefix="Util" type="Johnny.Utility"/>
        <add expressionPrefix="SQL" type="Johnny.SqlHelper"/>
      </expressionBuilders>
    </compilation>

事實上這個方法不只可以應用於設定 SqlDataSource 的 Connection String; 它實際上可以套用到任何伺服器控制項。例如以下這個控制項


<asp:Label runat="server" Text='<%$ Util: Johnny.Utility.DeveloperName %>' />

此處的 Util 是依照上述寫法, 寫在 Johnny.Utility 這個類別的 Prefix, 而 Johnny.Utility.DeveloperName 則是這個類別的一個文字屬性。

因此, 這篇文章所介紹的程式寫法, 可以應用在許多不同的情境之下。

參考:

  1. connectionStrings 的 add 項目 (ASP.NET 設定結構描述)
  2. ExpressionBuilder.ExpressionPrefix 屬性
  3. ExpressionBuilder.GetCodeExpression 方法
  4. CodeExpression 類別
  5. THE CODEEXPRESSIONBUILDER


Dev 2Share @ 點部落