在我目前的工作中, 環境略為有點複雜, 牽涉到開發、測試和正式環境, 而使用的資料庫伺服器也有兩到三個。如果能夠動態地切換不同環境下所使用的資料庫連線字串, 一定對工作相當有幫助。當然, 如果我都寫 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 則是這個類別的一個文字屬性。
因此, 這篇文章所介紹的程式寫法, 可以應用在許多不同的情境之下。
參考:
- connectionStrings 的 add 項目 (ASP.NET 設定結構描述)
- ExpressionBuilder.ExpressionPrefix 屬性
- ExpressionBuilder.GetCodeExpression 方法
- CodeExpression 類別
- THE CODEEXPRESSIONBUILDER