现在我们创建一个Lig工程 - Litelog
2.1 创建Lig服务
_________________________________________________________________________________
不好意思。我尽最大的程度说明清楚问题。 Lig这些内容是写How to use the WCF rather than to explain what the WCF is.
所以以跳跃性比较大,如果有些WCF基础最好。如果想知道Details,建议看Artech的《WCF全面解析》或search他的cnblog,这是最好的解决问题之道。
不过有一点你会高兴,这儿会提供这些文章与源代码工程.
OpenSource codes provided with you here .Thanks2.1 .1Lig工程创建 - Litelog
如图 2.1 .1所示 创建一个名为Litelog的空solution,同时添加如下四个新建项目: 1)LigServer: WCF服务端程序。控制台程序。Console Application 2)ILigAgent: WCF服务接口(服务契约)。库类代码。ClassLibrary 3)LigAgent: WCF服务实现,继承ILigAgent,借宿于LigServer进程。库类代码。ClassLibrary 4)LigClient: WCF客户端测试用例。控制台程序。Console ApplicationWCF创建遵循SOA标准中要求对契约编程,而不是实现。WCF通过接口定义服务契约,通过数据契约定义接口出现的参数,通常数据契约(非简单变量)出现的参数会在Server-Client之间共享。
因为WCF是基于特性的编程,如果你了解对象关系模型(ORM) ,就知道.net中所有继承于Attribute类的类是如何影响方法运行的。
图 2.1.1 Lig工程结构
所以,首先,我们看WCF服务接口内部是什么样子的。
2.1.2 创建WCF服务接口 – ILigAgent 在ILigAgent项目中添加两个文件夹:DataMembers与Interfaces,分别用来存放数据契约与服务契约。目前只含LigLevel.cs与ILigAgent.cs两个项目文件。 LigLevel : 枚举变量。Lig日志级别。
public enum LigLevel { Info = 0, Debug = 1, Warn = 2, Error = 3, Fatal = 4 }
图2.1.2 ILigAgent 服务契约结构
ILigAgent : 服务契约。一共含 8 个操作接口。
注意:
1) LigFatal有一个重载方法,这儿显式应用OperationContract特性中的Name属性加以区分。
否则,WCF服务接口中使用默认OperationContract特性是不允许重载方法出现的。在用SvcUtil.exe生成LigAgent代理类时时你会看到
void LigFatal(string message, Exception ex)
变成
void LigFatalex(string message, Exception ex)
了。讲LigServer生存代理类与客户端配置时后面会提到
2) Initialize用于初始化Lig路径。
SayHelloToServer则用于客户端创建通道(ChannelFactory)成功后测试Server是否在线,在创建LigClient时会解释SayHelloToServer存在的意义。
[ServiceContract] public interface ILigAgent { [OperationContract] void LigMessage(string message, LigLevel level); [OperationContract] void LigInfo(string message); [OperationContract] void LigDebug(string message); [OperationContract] void LigWarn(string message); [OperationContract] void LigError(string message); [OperationContract(Name = "LigFatal")] void LigFatal(string message); [OperationContract(Name = "LigFatalex")] void LigFatal(string message, Exception ex); [OperationContract] void SayHelloToServer(string message); [OperationContract] void Initialize(string ligPath); }2.1.3 创建WCF服务
LogAgent
如图2.1.3所示。创建WCF服务实现,实现ILigAgent服务接口。LigAgent将会用于借宿于LigServer进程。
关于服务行为特性说明(ServiceBehavior):
实例模式:PerSession。这儿采用一个代理,一个实例。
并发模式:Multiple。多线程。
这两个参数与WCF并发与实例管理相关。这儿的配置是因Lig的客户可能需要写不同的文件,同时要考虑多个Lig客户端存在会有异步调用的需求,以加快日志书写时间,提高LigServer的性能。从客户端需求与性能上考虑采用一个代理,一个实例,并发模式采用多线程。
图 2.1.3 创建WCF服务实现
2.1.4 LigCore - EmitFileMessage
对于OOP,其中有一条“依赖倒置”原则 其实,对C#本身面言,编程中,我们抽象出的更多的是interface而不是abstract类。我们个人喜欢用interface而不是单单的abstract类去实践“依赖倒置”原则。但有时还是有例外的,比如累量级的ORM PetaPoco对不同的数据库对象Privider,interface在.net中已经存在,所以我们会写abstract类为所有不同Privider的基类。说多了。
1)为了减小LigAgent的本身代码负担,我们创建另外一个类,LigCore,专门负责文件书写工作,并用Mutex保证线程操作安全,所有日志操作由EmitFileMessage完成。2)同时提供一个EmitFileMessage的非静态重载版本。
(此处文件操作用简单的StreamWriter实现。Log4net采用FileStream + TextWriter实现,同时采用Mutex保证文件操作线程安全)图2.1.4 LigCore文件操作
public void LigMessage(string message, LigLevel level){ string formatMsg = LigCore.LiggedTime + level.ToString() + " " + message; new LigCore().EmitFileMessage(formatMsg, this.LigPath, true);}
EmitFileMessage的非静态重载版本
public void EmitFileMessage(string message, string fullpath, bool isAppending) { StreamWriter writer = null; try { writer = new StreamWriter(fullpath, isAppending); writer.Write(message); writer.Flush(); } catch (Exception ex) { // ignore } finally { if (writer != null) { writer.Close(); } writer = null; } }
2.2 创建LigServer
_________________________________________________________________________________
2.2.1 关于Lambda表达式
先暂时不用去理解这儿出现的Lambda表达式:
(sender,e)=>{}
不过有必要说下,Lambda表达式,linq与扩展方法在.net3.0引入的新特性会大在简化你的编程时间。
如果应用Sql,Linq是最佳的方式。
你懂得Lambda表达式在创建临时方法的好处,linq查询对泛型委托的极致应用带来的对for/foreach/while的简化操作,扩展方法对现有类功能扩展的方便之处。如果你构建超时方法,事件处理等通用类,对Lambda表达式,linq查询与扩展方法会变成最常用的操作。
2.2.2 SvcUtil工具生成客户端代理类与客户端配置
1)启动LigServer,打开控制台,进入SvcUtil.exe所在的目录,输入:
SvcUtil.exe http://127.0.0.1:7023/Lig.vivitue.LigMetadata
2) 回车。如果你对SvcUtil的使用没有问题,你会看到生成的LigAgent.cs文件与output.config配置。把output.config改成App.config后就可以加入到客户端LigClient中使用了。
3) 同时在浏览器中输入http://127.0.0.1:7023/Lig.vivitue.LigMetadata你就能看到LigServer发布的元数据了。4)打开Output.config与LigAgent代理类。
不清楚svcutil的,baidu或google下。太多文章讲svcutil如何使用
用过svcutil的人都看过这种东西,看着LigAgent与output.config是不是有些乱?很多人看过这种文件。
我要说的是,对IIS借宿的基于Web访问的WCF服务,这是一种很好的方式,但对window form程序,这并不好,不利于代码后期维护。下面这两个文件在我们的客户端中都不会出现。
我们将采用一种新的方式来编写我们客户端的配置与用接口取代代理类。
图 2.2.1 LigServer
图 2.2.2 SvcUtil工具生成客户端代理类与配置
图 2.2.3 生成的 Output.config与LigAgent代理类
2.2.3 服务端配置文件- App.config服务端用的配置创建的WCF项目默认采用App.config或Web.config(IIS服务用)作为服务端与客户端的配置文件。该文件编译后会与*.exe文件放在一起。变成*.exe.config,在WCF配置启动时ServiceHost内部会自动加载。如果想改变配置文件路径与名称,则你得重写ServiceHost类与ChannelFactory<T>类了,后面讲《自定义配置》会解释。
关于WCF配置中WCF服务的行为 behavior WCF中,定义behavior用于设置服务运行时的特性,此处,我们通过指定ServiceMetadataBehavior使WCF服务对外公布Metadata,这样我们可以通过http形式访问。 同时用SvcUtil.exe工具生存代理类与代理类的配置文件output.config (客户端使用的)。 1)在WCF中,behavior被定义为Attribute 2)ServiceBehavior与OperationBehavior是最常用的behavior (在前面的接口中你看到的WCF服务接口的特性性应用) 3)一个WCF服务可以有多种行为4)但在配置中,一个WCF服务地址默认情况下只能指定一个行为(behaviorConfiguration),如果想一个WCF服务地址指定多种行为,此时你得扩展ServiceHost类了。
通过ServiceHost中的Description.Behaviors属笥添加多种行为。后面扩展ServiceHost类时会提到ServiceHost中的Description属性。图 2.2.3 客户端配置
2.3 创建LigClient的代码
_________________________________________________________________________________ 2.3.1 在客户端应用ILigAgent – 接口编程WCF依赖统一的服务接口在客户端与服务端进行通信,我们完全可以用ILigAgent来取代用SvcUtil工具自动生成的代理类LigAgent。
了解这点后,我们可以在LigClient添加ILigAgent接口,而不是应用自动生成了LigAgent代理类,那样代码太乱了,同时不太符合OOP中“依赖倒置”原则采用基于抽象编程的概念。这儿LigClient用接口,而不是实现类LigAgent。
2.3.2 创建 LigManager 我们创建两个方法。ConnectServer与DisconnectServer用来连接LigServer服务与断开LigServer服务。 采用ChannelFactory<T>来创建客户端通道同时获取客户端代理实例。看到红色的代码:
this.iLig.SayHelloToServer("LigManager Connecting : ClientHash : " + this.GetHashCode().ToString());如果LigServer没有启动,ConnectServer会阻塞到这行代码,直到时间达到配置中的超时,会引发一个超时异常。
在WCF中。ChannelFactory<T>创建通道与服务端存在与否无关,也就是说,如果LigServer没有启动,ChannelFactory<T>创建通道也会成功,所以我们不能用创建通道成功与否来表明WCF连接是否已经成功建立,我们必须通过Client调用Server的服务来确保WCF已经连接。 WCF服务只有在客户端第一次调用服务的时候才会创建实例为客户端服务。我们为了确保LigServer已经启动,所以加入了SayHelloToServer代码去调用服务,如果调用成功,LigClient与LigServer才算正式建立成功。 然后加入配置,编写如下客户端代码就可以工作了
图 2.3.1 LigManager
2.3.3 创建Client
1) 我们采用LigManager中的静态资源来获取ILigAgent实例写日志。这样Client代码就变得很简单了。
2) 你可能注意到了,在这里我们并没有用ILigAgent的Initialize接口初始化Lig文件路径,而是用了系统默认路径。因为我要让不同的客户端去写同一个文件,而不是不同文件。 3) 以后的章节中我们会对ILigAgent进行改进,ILigAgent接口暴露的东西还是太多了,把WCF特性暴露了。 4) 同时使用ILigAgent也太过于复杂,因为使用LigClient我们自己还得创建一个LigManager,简简单单的写个日志,这样做使LigServer客户端的使用有些麻烦。别担心,在后面的章节中,你会看到,我们会解决这儿的所有问题,我们会让LigManager不复存在。5) 写日志我们只需要这样
ILigger lig= new Ligger(“ligfile”); lig.Info(“YourInfo here”);
图 2.3.2 LigClient
图 2.3.3 LigClient 客户端配置
2.4 Lig系统测试
_________________________________________________________________________________
启动LigServer,同时启动4个LigClient,4个Client同时向默认文件Lig.lg中写入日志。。。
图 2.4.1 Lig系统测试
Lig系统已经工作
好了。到现在为止,这个经量级的Log已经完成。并且能够很好的稳定工作。但到现在为止,它还不够优秀,表现在:
1) 我们还没有考虑,这个LigServer到底可以同时连接多少个LigClient。
2) 同样还有没考虑,如果LigClient如果出现异常怎么办。
3) 也没有解释LigServer控制台中出现的HashCode是什么东东
4) LigServer与LigClient双向通信会是什么样子?服务端监控是什么样子?
在第三节中我们会解答这些所有的问题。。。
本节的源代码下载:
_________________________________________________________________________________
Litelog - WCF 项目应用连载[2] - 创建Lig日志系统 C# 源代码.rar
_________________________________________________________________________________
MB CSDN审核资源真TM慢。
参考引文:
[1] Artech.WCF全面解析[M].2012 [2] O'Reilly.WCF编程[M].2009 [3] Adnrew Trolesen.C#与.net3.5/4高级程序设计[M].2009/2013