Class Library使用DataContext時的連線字串設定問題
5 |
在Visual Studio中使用LINQ to SQL或EF時,拖拉資料庫產生Entity類別,VS會幫忙打理儲存連線字串。在小型ASP.NET專案裡,*.dbml被直接加入網站專案,VS就順理成章地在web.config <connectionStrings />中加入連線字串,實際上線時,將web.config裡的設定改連正式資料庫即可。
但當專案規模變大,我們常會將資料存取相關的程式移到獨立的DAL專案(Class Library),因此*.dbml / *.edmx等也被歸在此專案中,此時要如何處理連線字串設定?
首先,設計階段採用的資料庫連線字串也被保存在DAL專案的app.config(另外還有個Properties/Settings.Settings物件,提供以物件屬性方式存取這些參數,例如: Properties.Settings.Default.PlaygroundConnectionString)。因此我們在DAL專案的app.config中可以看到如下設定:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="ClassLibrary1.Properties.Settings.PlaygroundConnectionString"
connectionString="Data Source=(local);Initial Catalog=Playground;
Integrated Security=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
有趣的問題來了,我們ASP.NET專案中參照了ClassLibrary1,然後就可以大方地用new DataClass1DataContext()建立DataContext,接著LINQ到不亦樂乎:
不過,仔細一看web.config,咦? 只看到<connectionStrings />? 代表未設任何連線字串,而ClassLibrary1.dll也沒有夾帶config檔到ASP.NET專案,程式居然知道如何要連上哪一台資料庫,會不會太神了? 為追根究底,追一下程式:
先在DataClasses1.designer.cs找到DataClass1DataContext()建構式
public DataClasses1DataContext() :
base(global::ClassLibrary1.Properties.Settings.Default.
PlaygroundConnectionString, mappingSource)
{
OnCreated();
}
原來它用了前述的Settings.Settings物件屬性模型去取連線字串,再追到Settings.Designer.cs
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute(
global::System.Configuration.SpecialSetting.ConnectionString)]
[global::System.Configuration.DefaultSettingValueAttribute(
"Data Source=(local);Initial Catalog=Playground;Integrated Security=True;")]
public string PlaygroundConnectionString {
get {
return ((string)(this["PlaygroundConnectionString"]));
}
}
謎底揭曉! Settings.Settings裡的屬性會與.NET的設定檔運作機制配合,當web.config或程式名.exe.config中有相關設定時,會自動讀取設定檔的連線字串;若config檔案未指定該連線字串,則可透過DefaultSettingValueAttribute取得預設值。這就是為什麼web.config沒給connectionString,程式仍可運作的理由。換句話說,當我們在web.config中加入name="ClassLibrary1.Properties.Settings.PlaygroundConnectionString"的連線字串設定(可以參考ClassLibrary1專案的app.config)後,程式就會以web.config中的連線字串為準。
最後提醒一點,VS處理連線字串時預設會以明碼方式保存在config中,若連線字串包含帳號密碼,就不符合資安要求,關於這部分可自行寫函數做加解密處理,或者直接使用ASP.NET內建的connnectionStrings加密機制保護。避免機密資訊大喇喇地在攤在檔案裡,是維護系統安全的重要原則,請大家多加留意。
【2010-09-23更新】System.Configuration.DefaultSettingValueAttribute的做法雖可提供未設定web.cofig時的預設值,但也可能導致未加密連線字串被Build進dll/exe而有外流風險,正式部署前宜將相關連線資訊移除再編譯,會更加安全。感謝ChrisTorng補充!
Comments
# by ChrisTorng
為了避免編譯後的 exe/dll 中包含敏感資訊如連線字串,我有研究過方法,分享給大家。 只要在 web.config 中保留連線字串,Settings 設定畫面中將連線字串內容清掉,即可將 Settings.Designer.cs 中內含的連線字串清掉,避免編譯後的 exe/dll 中包含敏感資訊,但執行時又有 web.config 連線字串可以取得。 另外日後 web.config 中有修改時,開啟 Settings 設定畫面時,會問要不要以 web.config 的最新內容來更新 Settings 設定,此時答「否」就不會重新將 web.config 的內容複製到 Settings 設定畫面中了...
# by Jeffrey
to ChrisTorng,謝謝你的寶貴心得分享!
# by Eric
您好,我有另一個想法,會用這樣的方法不外乎是DAL專案可能會被多個WEB專案所引用,如此ConnectionString只要在DAL內設定好,其餘每個WEB專案都不用在web.config內重新設定一次,較易維護。 而拿掉DefaultSettingValueAttribute,又在每個WEB專案內重新設定web.config,就失去了使用此方法的意義,如此還不如不要那麼麻煩去設定DAL內的Settings,app.config連線字串的名稱還可以比較簡短不是嗎? ^_^
# by Jeffrey
to Eric,在我的工作環境,上線OP與RD是兩組不同人員,連線字串的設定是OP的工作(RD不知道正式資料庫的帳號密碼,也不可能直接寫在程式碼裡),當系統內有多個DAL,多個連線字串集中放在web.config有其好處,一次修改搞定。(通常還需加密 http://blog.darkthread.net/post-2015-11-28-encrypt-ef-connstring.aspx ) 以上經驗分享。
# by Eric
了解了~很實用的方法!!感謝您的快速回覆!! ^_^