之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即CodeDOM+Custom Tool和T4。对于ASP.NET应用,你还有第三种选择——自定义BuildProvider。[文中涉及的源代码从这里下载]
目录 一、BuildProvider是什么? 二、将XML表示的消息转换成VB.
之前写了一些关于代码生成的文章,提供了两种不同方式的代码生成解决方案,即
CodeDOM+Custom Tool
和
T4
。对于ASP.NET应用,你还有第三种选择——自定义BuildProvider。[文中涉及的源代码从
这里
下载]
目录
一、BuildProvider是什么?
二、将XML表示的消息转换成VB.NET或者C#代码
三、将XML转换成CodeDOM
四、自定义BuildProvider
五、BuildProvider的应用
一、BuildProvider是什么?
对于ASP.NET应用的开发者来说,你可能不知道什么是BuildProvider,但是你几乎无时无刻不在使用它所带来的代码生成机制。当你创建一个.aspx文件的时候,为什么会自动创建对应源代码?当你在该.aspx页面中以XML的方式添加一个按钮,源代码中为什么会自动添加一个同名的属性。实际上,ASP.NET就是通过一个特殊的BuildProvider实现了将.aspx文件内容转换成相应的源代码,这个特殊的.aspx文件就是:PageBuildProvider。基于不同的文件类型,ASP.NET会采用不同的BuildProvider进行源代码的生成。比如UserControlBuildProvider和MasterPageBuildProvider分别实现了基于用户控件文件(.ascx)和母板页(.master)的源代码生成。你可以通过查看%Windows%\Microsoft.NET\Framework\v2.0.50727\CONFIG\web.config看看在默认情况下使用的BuildProvider以及它基于的源文件类型(扩展名)。
1
:
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
2
:
<
configuration
>
3
:
<
system.web
>
4
: ... ...
5
:
<
compilation
>
6
:
<
buildProviders
>
7
:
<
add extension
=
"
.aspx
"
type
=
"
System.Web.Compilation.PageBuildProvider
"
/>
8
:
<
add extension
=
"
.ascx
"
type
=
"
System.Web.Compilation.UserControlBuildProvider
"
/>
9
:
<
add extension
=
"
.master
"
type
=
"
System.Web.Compilation.MasterPageBuildProvider
"
/>
10
:
<
add extension
=
"
.asmx
"
type
=
"
System.Web.Compilation.WebServiceBuildProvider
"
/>
11
:
<
add extension
=
"
.ashx
"
type
=
"
System.Web.Compilation.WebHandlerBuildProvider
"
/>
12
:
<
add extension
=
"
.soap
"
type
=
"
System.Web.Compilation.WebServiceBuildProvider
"
/>
13
:
<
add extension
=
"
.resx
"
type
=
"
System.Web.Compilation.ResXBuildProvider
"
/>
14
:
<
add extension
=
"
.resources
"
type
=
"
System.Web.Compilation.ResourcesBuildProvider
"
/>
15
:
<
add extension
=
"
.wsdl
"
type
=
"
System.Web.Compilation.WsdlBuildProvider
"
/>
16
:
<
add extension
=
"
.xsd
"
type
=
"
System.Web.Compilation.XsdBuildProvider
"
/>
17
:
<
add extension
=
"
.js
"
type
=
"
System.Web.Compilation.ForceCopyBuildProvider
"
/>
18
:
<
add extension
=
"
.lic
"
type
=
"
System.Web.Compilation.IgnoreFileBuildProvider
"
/>
19
:
<
add extension
=
"
.licx
"
type
=
"
System.Web.Compilation.IgnoreFileBuildProvider
"
/>
20
:
<
add extension
=
"
.exclude
"
type
=
"
System.Web.Compilation.IgnoreFileBuildProvider
"
/>
21
:
<
add extension
=
"
.refresh
"
type
=
"
System.Web.Compilation.IgnoreFileBuildProvider
"
/>
22
:
<
add extension
=
"
.svc
"
type
=
"
System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
"
/>
23
:
<
add extension
=
"
.xoml
"
type
=
"
System.ServiceModel.Activation.WorkflowServiceBuildProvider, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
"
/>
24
:
</
buildProviders
>
25
:
</
compilation
>
26
:
</
system.web
>
27
:
</
configuration
>
既然ASP.NET可以通过相应的BuildProvider为不同类型的文件生成相应的源代码,我们自然也能自定义BuildProvider实现我们希望的代码生成机制。为了让读者和之前提供的两种方式的代码生成机制作一个对于,我们依然采用相同的应用场景:将以XML表示的数据转换成代码,以实现强类型编程。
二、将XML表示的消息转换成VB.NET或者C#代码
可能有些人没有看过之前的文章,所以在这里我再次简单介绍一些我们需要通过代码生成机制实现的场景:无论对于怎么样的应用,我们都需要维护一系列的消息。消息的类型很多,比如验证消息、确认消息、日志消息等。我们一般会将消息储存在一个文件或者数据库中进行维护,并提供一些API来获取相应的消息项。这些API一般都是基于消息的ID来获取的,换句话说,消息获取的方式是以一种“弱类型”的编程方式实现的。如果我们能够根据消息存储的内容动态地生成相应的C#或者VB.NET代码,那么我们就能够以一种强类型的方式来获取相应的消息项了。
比如说,现在我们定义了如下一个MessageEntry类型来表示一个消息条目。为了简单,我们尽量简化MessageEntry的定义,仅仅保留三个属性Id、Value和Category。Category表示该消息条目所属的类型,你可以根据具体的需要对其分类(比如根据模块名称或者Severity等)。Value是一个消息真实的内容,可以包含一些占位符({0},{1},…{N})。通过指定占位符对用的值,最中格式化后的文本通过Format返回。
1
:
public
class
MessageEntry
2
: {
3
:
public
string
Id {
get
;
private
set
; }
4
:
public
string
Value {
get
;
private
set
; }
5
:
public
string
Category {
get
;
private
set
; }
6
:
public
MessageEntry(
string
id,
string
value,
string
category)
7
: {
8
:
this
.Id
=
id;
9
:
this
.Value
=
value;
10
:
this
.Category
=
category;
11
: }
12
:
public
string
Format(
params
object
[] args)
13
: {
14
:
return
string
.Format(
this
.Value, args);
15
: }
16
: }
现在我们所有的消息定义在如下一个XML文件中,<message>XML元素代码一个具体的MessageEntry,相应的属性(Attribute)和MessageEntry的属性(Property)相对应。
1
:
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
2
:
<
messages
>
3
:
<
message id
=
"
MandatoryField
"
value
=
"
The {0} is mandatory.
"
category
=
"
Validation
"
/>
4
:
<
message id
=
"
GreaterThan
"
value
=
"
The {0} must be greater than {1}.
"
category
=
"
Validation
"
/>
5
:
<
message id
=
"
ReallyDelete
"
value
=
"
Do you really want to delete the {0}.
"
category
=
"
Confirmation
"
/>
6
:
</
messages
>
在上面的XML中,定义了两个类别(Validation和Confirmation)的三条MessageEntry。我们需要通过我们的代码生成工具生成一个包含如下C#代码的CS文件。
1
:
public
class
Messages {
2
:
public
class
Validation {
3
:
public
static
Artech.MessageCodeGenerator.MessageEntry MandatoryField
=
new
Artech.MessageCodeGenerator.MessageEntry(
"
MandatoryField
"
,
"
The {0} is mandatory.
"
,
"
Validation
"
);
4
:
public
static
Artech.MessageCodeGenerator.MessageEntry GreaterThan
=
new
Artech.MessageCodeGenerator.MessageEntry(
"
GreaterThan
"
,
"
The {0} must be greater than {1}.
"
,
"
Validation
"
);
5
: }
6
:
public
class
Confirmation {
7
:
public
static
Artech.MessageCodeGenerator.MessageEntry ReallyDelete
=
new
Artech.MessageCodeGenerator.MessageEntry(
"
ReallyDelete
"
,
"
Do you really want to delete the {0}.
"
,
"
Confirmation
"
);
8
: }
9
: }
三、将XML转换成CodeDOM
实际BuildProvider也是采用CodeDOM来定义代码的结构,
在这之前
我已经创建了一个CodeGenerator类实现了如何加载具有上述结构的XML,并生成一个体现最终代码结构的CodeCompileUnit对象。该CodeGenerator的所有代码的定义如下。
1
:
public
class
CodeGenerator
2
: {
3
:
public
CodeCompileUnit BuildCodeObject(XmlDocument messages)
4
: {
5
: var codeObject
=
new
CodeCompileUnit();
6
: var codeNamespace
=
new
CodeNamespace(
"
Artech.CodeDomGenerator
"
);
7
: codeObject.Namespaces.Add(codeNamespace);
8
: var codeType
=
new
CodeTypeDeclaration(
"
Messages
"
);
9
: codeNamespace.Types.Add(codeType);
10
: GenerateCatetoryClasses(codeType, messages);
11
:
return
codeObject;
12
: }
13
:
14
:
private
void
GenerateCatetoryClasses(CodeTypeDeclaration root, XmlDocument messageDoc)
15
: {
16
: var messageEntries
=
messageDoc.GetElementsByTagName(
"
message
"
).Cast
<
XmlElement
>
();
17
: var categories
=
(from element
in
messageEntries
18
: select element.Attributes[
"
category
"
].Value).Distinct();
19
:
20
:
foreach
(var category
in
categories)
21
: {
22
: var categoryType
=
new
CodeTypeDeclaration(category);
23
: root.Members.Add(categoryType);
24
:
25
:
foreach
(var element
in
messageDoc.GetElementsByTagName(
"
message
"
).Cast
<
XmlElement
>
().
26
: Where(element
=>
element.Attributes[
"
category
"
].Value
==
category))
27
: {
28
: GenerateMessageProperty(element, categoryType);
29
: }
30
: }
31
: }
32
:
33
:
private
void
GenerateMessageProperty(XmlElement messageEntry, CodeTypeDeclaration type)
34
: {
35
:
string
id
=
messageEntry.Attributes[
"
id
"
].Value;
36
:
string
value
=
messageEntry.Attributes[
"
value
"
].Value;
37
:
string
categotry
=
messageEntry.Attributes[
"
category
"
].Value;
38
:
39
: var field
=
new
CodeMemberField(
typeof
(MessageEntry), id);
40
: type.Members.Add(field);
41
: field.Attributes
=
MemberAttributes.Public
|
MemberAttributes.Static;
42
: field.InitExpression
=
new
CodeObjectCreateExpression(
43
:
typeof
(MessageEntry),
44
:
new
CodePrimitiveExpression(id),
45
:
new
CodePrimitiveExpression(value),
46
:
new
CodePrimitiveExpression(categotry));
47
: }
48
: }
四、自定义BuildProvider
现在我们才进行我们的重点,如何通过一个自定义的BuildProvider将以XML形式存储的消息列表转换成相应的C#或者VB.NET代码。为此我们创建一个名为MessageBuildProvider的类,MessageBuildProvider继承自抽象类BuildProvider。因为从XML到CodeDOM的转换已经实现在了上面的CodeGenerator类中,MessageBuildProvider的定义很简单。
1
:
public
class
MessageBuildProvider : BuildProvider
2
: {
3
:
public
override
void
GenerateCode(AssemblyBuilder assemblyBuilder)
4
: {
5
: var messageDoc
=
new
XmlDocument();
6
:
using
(var stream
=
this
.OpenStream())
7
: {
8
: messageDoc.Load(stream);
9
: }
10
: var codeObj
=
new
CodeGenerator().BuildCodeObject(messageDoc);
11
: assemblyBuilder.AddCodeCompileUnit(
this
, codeObj);
12
: }
13
: }
五、BuildProvider的应用
自定义的BuildProvider以配置的方式和源文件的类型(扩展名),在这里我们通过一个扩展名为.msg(不代表OutLook的消息文件)来表示上述的存储消息列表的XML。那么,你可以创建一个WebSite,并添加对定义了MessageBuildProvider的Dll引用或者项目引用。然后添加一个XML文件,并将扩展名改成.msg,然后定义如下一段XML。
1
:
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
2
:
<
messages
>
3
:
<
message id
=
"
MandatoryField
"
value
=
"
The {0} is mandatory.
"
category
=
"
Validation
"
/>
4
:
<
message id
=
"
GreaterThan
"
value
=
"
The {0} must be greater than {1}.
"
category
=
"
Validation
"
/>
5
:
<
message id
=
"
ReallyDelete
"
value
=
"
Do you really want to delete the {0}.
"
category
=
"
Confirmation
"
/>
6
:
</
messages
>
然后在Web.config中添加如下一段配置以建立MessageBuildProvider和源文件扩展名(.msg)之间的匹配关系。
1
:
<?
xml version
=
"
1.0
"
?>
2
:
<
configuration
>
3
:
<
system.web
>
4
:
<
compilation debug
=
"
false
"
targetFramework
=
"
4.0
"
>
5
:
<
buildProviders
>
6
:
<
add extension
=
"
.msg
"
type
=
"
Artech.MessageCodeGenerator.MessageBuildProvider, Artech.MessageCodeGenerator.Lib
"
/>
7
:
</
buildProviders
>
8
:
</
compilation
>
9
:
</
system.web
>
10
:
</
configuration
>
然后,你在任何该WebSite范围类进行编程的时候,就可以利用VS的职能感知感受到相应的代码已经生成。
为什么说“感受”得到代码已经被成功生成呢?这是因为不象之前介绍的两种代码生成方式,会显式地创建一个.cs或者.vb物理文件,并自动添加到项目文件。BuildProvider采用的是一种隐式代码生成机制。不过你通过Go to definition菜单可以得到整个生成代码的内容。如果你采用基于C#的WebSite,生成的代码时如下所示。由于CodeDOM的语言无关性,你也可以将MessageBuildProvider用于基于VB.NET的ASP.NET应用。
采用.Net Core技术框架开发的医院云LIS平台源码,B/S架构
基于B/S架构的医学实验室检验系统源码,整个系统的运行基于WEB层面,只需要在对应的工作台安装一个浏览器软件有外网即可访问。全套系统采用云部署模式,部署一套可支持多家医院检验科共同使用。
采用.Net Core新的技术框架、DEV报表、前端js封装、分布式文件存储、分布式缓存等,支持LIS独立部署,Docker部署等多种方式。
.Net线程同步技术解读
C#开发者(面试者)都会遇到lock(Monitor),Mutex,Semaphore,SemaphoreSlim这四个与锁相关的C#类型,本文期望以最简洁明了的方式阐述四种对象的区别。
.Net Mirco Framework 2007技术大会
最近公司很多项目都有大量嵌入式设备使用,由于WinCE系统相对较大,对硬件平台要求过高,所以对.Net MF一直比较关注。今天总算大开眼界了
原文:[.NET]使用十年股价对比各种序列化技术
1. 前言
上一家公司有搞股票,当时很任性地直接从服务器读取一个股票10年份的股价(还有各种指标)在客户端的图表上显示,而且因为是桌面客户端,传输的数据也是简单粗暴地使用Soap序列化。
【活动介绍】
微软爱开源,已是尽人皆知的事实。自从收购全球最大的开源社区 GitHub 之后,微软依旧使 GitHub 保持独立运营,并且通过此项举措,微软本身已经成为最大的社区服务者。
它是一种架构模式,提倡将大的单体系统,按业务拆分成一个个较小且独立的服务,服务与服务之前进行相互协作和配合。
针对互联网行业的蓬勃发展,需要支撑的业务越来越多,越来越大,单体程序越来越难以支撑,因此才出现了微服务的这种架构。