Fluent Configuration API 入門範例

以往,我們安裝好 Enterprise Library 之後,大都是在 Solution Explorer 中,以點右鍵的方式開啟組態設定視窗來為 App.config 加入所需之組態。這裡示範完全使用程式碼的方式,而且是透過 Fluent Configuration API 來協助我們於執行時期設定應用程式組態。同時會稍微提及 Enterprise Library 裡面的 DI 容器。

簡介

Fluent Configuration API 也是 Enterprise Library 的一部分,包在 Microsoft.Practices.EnterpriseLibrary.Common 組件內。透過這組 API,你可以自行撰寫程式來控制應用程式的組態。它的寫法挺有意思,是類似 jQuery 那樣,以串接(fluent)或接力的方式呼叫物件的方法。這種寫法,一旦串接的層次很多,不熟該 API 的人讀起來可能會覺得霧煞煞。

如果你的環境還沒有安裝 EntLib,請到官網下載最新版本。這裡我用的版本是 Enterprise Library 5.0 Optional Update 1。

使用 EntLib 工具來設定應用程式組態

若你已經知道怎麼使用 EntLib 工具來設定組態,而且只想看如何用程式碼來設定應用程式組態,請直接跳到下一節。

幾年前寫過幾篇 Enterprise Library Logging Application Block 的文章,當時使用的 EntLib 版本 v3.1。現在的操作方式也差不多,安裝好 Enterprise Library 之後,還是在 Solution Explorer 中,以點右鍵的方式為 App.config 加入所需之組態,如下圖:


接著會開啟 EntLib 組態設定工具。你可以在此視窗中,從主選單點 Blocks > Add Logging Settings 來加入 Logging Application Block 組態。參考下圖:


然後 App.config 就會像下圖這樣:


如果你只需要工具幫你產生組態項目,看到這裡就夠了。因為接下去的動作很簡單,只要在程式中呼叫 Logger.Write() 方法就可以輸出 log 訊息了。底下的範例是要示範如何完全以程式碼來設定這些組態。此範例會借助 Fluent Configuration API 來替我們完成這些工作。

使用 Fluent Configuration API 來設定應用程式組態

首先,為你的專案加入組件參考:以瀏覽檔案的方式找到並加入需要參考的 EntLib 組件檔案。預設情況下,EntLIb 組件是安裝在 c:\Program Files (x86)\Microsoft Enterprise Library 5.0\Bin\ 目錄下。此處範例需要加入的組件包括:
  • Microsoft.Practices.EnterpriseLibrary.Common (裡面有 Fluent Configuration API)
  • Microsoft.Practices.EnterpriseLibrary.Logging
  • Microsoft.Practices.ServiceLocation (因為需要ServiceLocator)
  • System.Configuration

接著,我把控制 Logging 組態的部分包在一個名叫 MyLogger 的靜態類別裡面,而且由這個類別來提供寫入 log 訊息的方法。如此一來,每當呼叫端的程式需要輸出 log,就可以這麼寫:

MyLogger.Write("測試 log 輸出。""Qoo");

其中第一個參數是 log 訊息,第二個參數是自訂的訊息分類。

MyLogger 的完整程式碼如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;

namespace FluentConfigDemo
{
    public static class MyLogger
    {
        static MyLogger()
        {
            Initialize();
        }

        private static void Initialize()
        {
            var cfgBuilder = new ConfigurationSourceBuilder();

            cfgBuilder.ConfigureLogging().WithOptions
                .DoNotRevertImpersonation()
                .LogToCategoryNamed("EventLog")
                .SpecialSources.AllEventsCategory
                .SendTo.EventLog("Event Log Listener")
                .FormatWith(new FormatterBuilder()
                    .TextFormatterNamed("Text Formatter")
                    .UsingTemplate("時間: {timestamp}{newline}訊息: {message}{newline}訊息分類: {category}"))
                .ToLog("Application")
                .UsingEventLogSource("FluentConfigDemo");

            var configSource = new DictionaryConfigurationSource();
            cfgBuilder.UpdateConfigurationWithReplace(configSource);
            EnterpriseLibraryContainer.Current
              = EnterpriseLibraryContainer.CreateDefaultContainer(configSource);
        }

        public static void Write(string message, string category)
        {
            Logger.Write(message, category);
        }
    }
}

這裡我是將 log 輸出至 Windows 事件日誌。如果你想要使用其他的 log 儲存方式,可以參考 MSDN 的範例,或者問一下 Google。

動態建立應用程式組態的工作是由 MyLogger 的 Initialize 方法完成。首先,它利用 ConfigurationSourceBuilder 物件來建立 logging 組態,組態建立完成之後,再將這些組態加入一個新建立的 DictionaryConfigurationSource 物件,接著再以此「組態來源」物件為基礎來建立一個新的「預設容器」,最後再將此預設容器指定為 EntLib 目前使用的容器......嗯,這是火星話嗎?

我恐怕沒辦法在這裡把 EntLib 容器相關的東西講得很清楚,僅摘列幾點,聊備一格:
  • EnterpriseLibraryContainer 類別基本上是個 Facade,是存取 server locator 的入口。這個 service locator 物件可以讓你將應用程式中欲使用的類別透過應用程式組態檔來定義,並且在執行時期動態建立組態檔中定義的類別的 instance。簡單講就是不用把所欲使用的具象類別(concrete class)寫死在程式裡面,以便將來修改組態檔就可以把類別換掉啦!
  • EnterpriseLibraryContainer.Current 屬性會傳回目前的 service locator 物件的參考,其型別為 IServerLocator。你可以透過此屬性來取得或設定 EntLib 目前所使用的 container 物件。對於不在乎或不需要額外處理 container 物件的人來說,這個屬性就挺方便的,因為當你第一次存取這個屬性時,如果預設的 service locator 物件還沒建立起來,EntLib 會幫你建立一個預設的 service locator,也就是 UnityServiceLocator。
  • UnityServiceLocator 是個 adapter,它把 EntLib 的預設容器 UnityContainer 轉換成更為通用的 IServiceLocator 介面。好處是,如果你不喜歡 UnityContainer,或哪天想要把預設容器換成別家品牌(例如 Castle WindsorSpring.NET frameworkStructureMap),你可以隨時將它換掉,因為你是針對 IServiceLocator 介面來寫程式,而非針對特定的容器實作品
  • 也許我該用另一篇筆記來整理這個部分,在此之前

注意:若 App.config 裡面已經有定義 <loggingConfiguration> 區段,那麼此區段會被我們程式動態產生的組態取代掉。只有 loggingConfiguration 區段會被取代,其他區段如 appSettings、connectionStrings 等並不會受到影響。

接著用一個 Console 應用程式專案來測試此類別:

    internal class Program
    {
        private static void Main(string[] args)
        {
            MyLogger.Write("測試 log 輸出。", "Qoo");
        }
    }

執行此程式之後,到 Windows 事件檢視器中看看是否有寫入一筆紀錄。結果如下圖(我的作業系統是英文版):

輸出至事件檢視器的 log 訊息

要特別說明的是,這個範例程式專案沒有 App.config 也可以跑,因為 MyLogger 已經完全用程式碼的方式來設定 Logging API 的組態了。如果專案本身已經有應用程式組態檔,當然也沒問題,只不過組態檔中有關 Logging API 的設定會被 MyLogger 蓋掉(參考前面的說明)。

小結

這個入門範例就到這裡。你可以進一步改善 MyLogger,將你需要的處理 logging 的邏輯都包在裡面。如此一來,呼叫端的程式碼便毋須引用 Microsoft.Practices.EnterpriseLibrary.Logging 命名空間。寫法很多,要不要多包這一層,就看個人需要而定了。

延伸閱讀

Post Comments

技術提供:Blogger.