为了降低本系统各个组件之间的耦合度,本系统将BLL层采用WCF技术发布为Web Service,以供UI层调用。
前面我们已经介绍过,为什么UI层不直接调用BLL层,而是要经过UI->Service.Wrapper->Service.Host->Service->BLL这样绕一大圈的方式来调用BLL层呢?
笔者认为至少有以下几个原因:
第一,直接调用会导致系统耦合度太高,任何后台的改动都会导致前台需要重新编译、发布,而这样做了之后,只要Contract不改变,则前台不用做任何改动;
第二,这样做了之后,比较适合SOA的理念,系统的扩展性、交互性和灵活性大大提高;
第三,直接调用会导致Solution中会有过多的Project,编译非常慢,导致开发人员的时间浪费过多。
下面让我们来看看WCF是如何在系统中得到应用的。
首先,我们需要事先实现一个Contract,让相关的各个组件共同遵守。一个Contract,实际上就是一个interface,为了使它能用于WCF,我们需要添加ServiceContract、OperationContract标识。如果该interface用到了一些自定义的Info类,则需要使用ServiceKnownType一一指明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.ServiceModel; 7 using System.Runtime.Serialization; 8 using Eallies.OA.Service.Contract.Fault; 9 using Eallies.OA.Info; 10 11 namespace Eallies.OA.Service.Contract 12 { 13 [ServiceContract] 14 [ServiceKnownType(typeof(EmployeeInfo))] 15 public interface IEmployeeContract 16 { 17 [OperationContract] 18 [FaultContract(typeof(FaultInfo))] 19 void SaveEmployee(EmployeeInfo employeeInfo); 20 21 [OperationContract] 22 IList GetEmployees(); 23 } 24 } |
另外,FaultContract用于WCF的错误处理,我们可以在配置文件中将includeExceptionDetailInFaults设为true,这样我们就能将错误的详细信息通过Web Service传到UI层。而FaultInfo则是一个简单的类,用于保存需要传递的消息,如错误信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
1 <? xml version = "1.0" ?>
3 < system.serviceModel >
4 < behaviors >
5 < serviceBehaviors >
6 < behavior name = "metadataSupport" >
7 < serviceDebug includeExceptionDetailInFaults = "true" />
8 < serviceMetadata httpGetEnabled = "true" />
9 </ behavior >
10 </ serviceBehaviors >
11 </ behaviors >
12 </ system.serviceModel >
13 </ configuration >
|
之后,我们就可以用一个继承于Contract的类将BLL层进行包装。
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
|
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using Eallies.OA.Service.Contract; 7 using Eallies.OA.Info; 8 using Eallies.OA.BLL; 9 10 namespace Eallies.OA.Service 11 { 12 public class EmployeeService : IEmployeeContract 13 { 14 #region IEmployeeContract Members 15 16 public void SaveEmployee(EmployeeInfo employeeInfo) 17 { 18 try 19 { 20 EmployeeBLL bll = new EmployeeBLL(); 21 bll.SaveEmployee(employeeInfo); 22 } 23 catch 24 { 25 throw; 26 } 27 } 28 29 public IList GetEmployees() 30 { 31 try 32 { 33 EmployeeBLL bll = new EmployeeBLL(); 34 return bll.GetEmployees(); 35 } 36 catch 37 { 38 throw; 39 } 40 } 41 42 #endregion 43 } 44 } |
完成之后的这样一个类我们就可以采用WCF技术Host到合适的应用程序中了。本系统将WCF给Host到IIS中了。要做到这点,只需要创建一个ASP.NET Web Service Application,将该项目发布为虚拟目录。
具体的类则是通过svc文件来完成Host的。其代码则非常简单:
1
|
1 <%@ ServiceHost Language="C#" Debug="true" Service="Eallies.OA.Service.EmployeeService" %> |
这样一来,该BLL层的所有函数就发布成Web Service了。不论客户端的形式是怎样的,我们都可以调用这个Web Service来完成客户端的功能。当然,为了使客户端的调用更为简单,我们需要将Web Service进行进一步的包装。这个过程可以使用Microsoft自带的svcutil.exe工具来完成。
1
|
1 svcutil.exe "http://localhost/Eallies.OA.Service.Host/EmployeeHost.svc?wsdl" /o:"..Eallies.OA.Service.WrapperEmployeeWrapper.cs" /r:"..Eallies.OA.InfobinDebugEallies.OA.Info.dll" /r:"..Eallies.OA.Info.EnumbinDebugEallies.OA.Info.Enum.dll" /r:"..Eallies.OA.Service.Contract.FaultbinDebugEallies.OA.Service.Contract.Fault.dll" /n:*,Eallies.OA.Service.Wrapper /noConfig |
值得注意的是,如果我们的Web Service引用了其它的类,则svcutil.exe工具会帮我们将所有的这些类重新生成一次,当然,这不是我们期望的。为此,我们采用参数/r的方式将需要引用的各个dll传入,这样就可以避免svcutil.exe工具自动生成了。另外/r参数可以多次指定,这为多个dll的引用提供了可能。