介绍一款好用的基于.NET的配置文件设计工具

在进行框架开发的过程中,我们往往需要对配置文件的结构进行设计,以便产生一套完整的配置方案,供开发人员在使用框架时能对框架进行配置。对于某些大型的框架,其配置节点的结构可能相当复杂,比如某个配置节点(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按钮。

image

在完成Designer Surface的创建之后,我们可以看到在项目中多了一个.csd的文件,在Toolbox中,也出现了与配置文件设计相关的工具:

image

看上去是不是有点像ADO.NET Entity Framework的设计器?不错,这就是Microsoft Visualization & Modeling SDK给我们带来的强大功能:它允许开发人员设计自己的领域特定语言(Domain Specific Language, DSL),并以VSIX等扩展包的方式集成到Visual Studio开发环境中。

功能介绍

本文不打算讲解如何使用Configuration Section Designer来设计配置文件,只对其中的一些非常不错的功能进行介绍。

自动化代码与文件的生成

对于一款DSL来说,自动化代码生成不算是什么强大的功能,但是这款工具不仅仅会产生代码,而且还会产生与之相关的XSD Schema以及配置文件样本(Sample Configuration File),能让开发者直观地看到最终效果:

image

配置文件样本测试

在设计配置文件的过程中,你可以直接双击产生的.csd.config(比如上面的ConfigurationSectionDesigner1.csd.config)文件,然后在里面进行编辑,来测试你的设计是否正确。注意这个编辑过程是自带有智能感知的:

image

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特性以在用于描述源类型的元数据类型上进行特性设置。以下是这种实现方式的相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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相关特性的设置,然后会在产生的代码中添加相应的特性描述,大大减轻了开发者的负担。

image

 

产生的代码如下:

image

 

Configuration Section Designer应该还有很多不错的功能,时间关系我也没有进行深入研究,有兴趣的朋友不妨下载一个Configuration Section Designer体验一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值