在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

了解了~很實用的方法!!感謝您的快速回覆!! ^_^

Post a comment