介紹一款好用的基於.NET的設定檔設計工具

來源:互聯網
上載者:User

在進行架構開發的過程中,我們往往需要對設定檔的結構進行設計,以便產生一套完整的配置方案,供開發人員在使用架構時能對架構進行配置。對於某些大型的架構,其配置節點的結構可能相當複雜,比如某個配置節點(Configuration Element)可以有屬性,還可以在其下掛載多個其它的配置節點或者多個配置節點集合(Configuration Element Collection)。如果使用手動編寫代碼的方式來維護與配置相關的代碼,勢必會出現大量的重複勞動,比如,需要給每個配置屬性添加System.Configuration.ConfigurationPropertyAttribute特性,需要為各個配置節點集合採用相同的代碼編寫入模式(例如重寫其中的CreateElement等方法)。這樣做不僅耗時而且容易出錯。更進一步,Visual Studio支援智能感知技術,如果我們在設定檔編輯器上設定了所要用到的配置資訊XSD Schema檔案,我們就可以利用智能感知方便快速地編寫設定檔。然而,如果我們的配置節點採用手工代碼維護,那麼在編寫完代碼之後,還需要另外編寫一套XSD Schema檔案,以使得開發人員在使用我們的架構時,能夠享受到智能感知帶來的便捷,這樣做不僅工作量大,而且一旦配置資訊結構變得複雜,我們就很難確保代碼與XSD Schema之間的對應關係。

今天我向大家介紹一款個人覺得比較不錯的基於.NET的設定檔設計工具:Configuration Section Designer。它是一款使用Microsoft Visualization & Modeling SDK開發的面向設定檔設計領域的領特定領域語言(DSL,有關DSL的知識,我會在後續的部落格文章中向大家介紹)。如果你使用的是Visual Studio 2005/2008,你可以去Configuration Section Designer的首頁下載安裝包。如果你使用的是Visual Studio 2010,那麼你還可以使用VS2010的Extension Manager來找到這個設計工具。

下載與安裝

可以到Configuration Section Designer的下載頁面下載並安裝該工具。如果你使用的是Visual Studio 2005/2008,你將得到一個EXE的安裝程式;如果你使用的是Visual Studio 2010,你將得到一個VSIX的擴充包。我使用的是Visual Studio 2010,因此接下來都會以Visual Studio 2010進行介紹。

在得到VSIX擴充包後,雙擊直接開啟運行就可以將其安裝到Visual Studio 2010的開發環境中。

建立設定檔項目

首先你可以使用Visual Studio 2010隨便建立一個項目(比如Class Library或者Console Application都可以),然後在這個項目上單擊滑鼠右鍵,選擇Add –> New Item菜單,這將開啟Add New Item對話方塊,在Installed Templates –> Visual C# Items節點下,找到ConfigurationSectionDesigner,更改名稱後單擊Add按鈕。

在完成Designer Surface的建立之後,我們可以看到在項目中多了一個.csd的檔案,在Toolbox中,也出現了與設定檔設計相關的工具:

看上去是不是有點像ADO.NET Entity Framework的設計器?不錯,這就是Microsoft Visualization & Modeling SDK給我們帶來的強大功能:它允許開發人員設計自己的領特定領域語言(Domain Specific Language, DSL),並以VSIX等擴充包的方式整合到Visual Studio開發環境中。

功能介紹

本文不打算講解如何使用Configuration Section Designer來設計設定檔,只對其中的一些非常不錯的功能進行介紹。

自動化代碼與檔案的產生

對於一款DSL來說,自動化代碼產生不算是什麼強大的功能,但是這款工具不僅僅會產生代碼,而且還會產生與之相關的XSD Schema以及設定檔樣本(Sample Configuration File),能讓開發人員直觀地看到最終效果:

設定檔樣本測試

在設計設定檔的過程中,你可以直接雙擊產生的.csd.config(比如上面的ConfigurationSectionDesigner1.csd.config)檔案,然後在裡面進行編輯,來測試你的設計是否正確。注意這個編輯過程是內建有智能感知的:

Windows Forms設計器的支援

一個專業的開發架構在向使用者提供配置相關的代碼以及XSD Schema的同時,還應該為使用者提供方便的設定檔編輯器(例如Microsoft Patterns & Practices Enterprise Library通常都會帶有面向各種Application Block的配置編輯器)。試想我們將用Windows Forms及其Property Grid控制項來設計一款設定檔編輯器,在將設定物件綁定到Property Grid時,Property Grid會通過反射將該對象下所有的屬性都顯示出來。然而對於配置編輯器而言,我們不僅需要控制設定物件中各個屬性的顯示方式,而且還需要對這些屬性進行一些描述和歸類。如果是手工維護架構中的配置代碼,這個問題好解決:直接向每個屬性手工添加諸如System.ComponentModel.DescriptionAttribute、System.ComponentModel.BrowsableAttribute等特性即可。但如果整個配置代碼都是由某工具自動化產生的,那麼你就不能直接在產生的程式碼上就行手工修改,而只能通過System.ComponentModel.DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider、System.ComponentModel.CustomTypeDescriptor以及System.ComponentModel.PropertyTypeDescriptor類來擴充MetadataType Description,然後使用System.ComponentModel.DataAnnotations.MetadataTypeAttribute特性以在用於描述源類型的中繼資料類型上進行特性設定。以下是這種實現方式的相關代碼:

public class MyAssociatedMetadataTypeTypeDescriptionProvider : AssociatedMetadataTypeTypeDescriptionProvider{    public MyAssociatedMetadataTypeTypeDescriptionProvider(Type type)        : base(type) { }    public MyAssociatedMetadataTypeTypeDescriptionProvider(Type type, Type associatedMetadataType)        : base(type, associatedMetadataType) { }    private ICustomTypeDescriptor Descriptor { get; set; }    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)    {        if (null == this.Descriptor)            this.Descriptor = new MyCustomTypeDescriptor(base.GetTypeDescriptor(objectType, instance));        return this.Descriptor;    }}public class MyCustomTypeDescriptor : CustomTypeDescriptor{    public MyCustomTypeDescriptor(ICustomTypeDescriptor wrappedTypeDescriptor)    {        this.WrappedTypeDescriptor = wrappedTypeDescriptor;    }    private ICustomTypeDescriptor WrappedTypeDescriptor { get; set; }    public override AttributeCollection GetAttributes()    {        return this.WrappedTypeDescriptor.GetAttributes();    }    public override PropertyDescriptorCollection GetProperties()    {        PropertyDescriptorCollection properties = this.WrappedTypeDescriptor.GetProperties();        List<PropertyDescriptor> list = new List<PropertyDescriptor>();        foreach (PropertyDescriptor descriptor in properties)            list.Add(new MyPropertyDescriptor(descriptor));        return new PropertyDescriptorCollection(list.ToArray(), true);    }    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)    {        return this.GetProperties();    }}public class MyPropertyDescriptor : PropertyDescriptor{    public MyPropertyDescriptor(PropertyDescriptor wrappedPropertyDescriptor)        : base(wrappedPropertyDescriptor)    {        this.WrappedPropertyDescriptor = wrappedPropertyDescriptor;    }    private PropertyDescriptor WrappedPropertyDescriptor { get; set; }    public override void AddValueChanged(object component, EventHandler handler)    {        this.WrappedPropertyDescriptor.AddValueChanged(component, handler);    }    public override bool CanResetValue(object component)    {        return this.WrappedPropertyDescriptor.CanResetValue(component);    }    public override Type ComponentType    {        get        {            return this.WrappedPropertyDescriptor.ComponentType;        }    }    public override bool IsReadOnly    {        get        {            return this.WrappedPropertyDescriptor.IsReadOnly;        }    }    public override object GetValue(object component)    {        return this.WrappedPropertyDescriptor.GetValue(component);    }    public override Type PropertyType    {        get        {            return this.WrappedPropertyDescriptor.PropertyType;        }    }    public override void RemoveValueChanged(object component, EventHandler handler)    {        this.WrappedPropertyDescriptor.RemoveValueChanged(component, handler);    }    public override void ResetValue(object component)    {        this.WrappedPropertyDescriptor.ResetValue(component);    }    public override void SetValue(object component, object value)    {        List<Attribute> attributes = new List<Attribute>();        this.FillAttributes(attributes);        foreach (Attribute attribute in attributes)        {            ValidationAttribute validationAttribute = attribute as ValidationAttribute;            if (null == validationAttribute)                continue;            if (!validationAttribute.IsValid(value))                throw new ValidationException(validationAttribute.ErrorMessage, validationAttribute, component);        }        this.WrappedPropertyDescriptor.SetValue(component, value);    }    public override bool ShouldSerializeValue(object component)    {        return this.WrappedPropertyDescriptor.ShouldSerializeValue(component);    }    public override bool SupportsChangeEvents    {        get        {            return this.WrappedPropertyDescriptor.SupportsChangeEvents;        }    }}// 以下是使用方式:[MetadataType(typeof(ApplicationElementMetadata))]public partial class ApplicationElement{    static ApplicationElement()    {        TypeDescriptor.AddProvider(            new MyAssociatedMetadataTypeTypeDescriptionProvider(                typeof(ApplicationElement)), typeof(ApplicationElement));    }}public class ApplicationElementMetadata{    [Description("Indicates the provider of the Application.")]    public string Provider { get; set; }}

 

然而對於Configuration Section Designer而言,這種繁雜的實現方式已經不複存在。它本身就支援Component Model相關特性的設定,然後會在產生的代碼中添加相應的特性描述,大大減輕了開發人員的負擔。

 

產生的代碼如下:

 

Configuration Section Designer應該還有很多不錯的功能,時間關係我也沒有進行深入研究,有興趣的朋友不妨下載一個Configuration Section Designer體驗一下。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.