为了降低本系统各个组件之间的耦合度,本系统将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.Contract12 {13 [ServiceContract]14 [ServiceKnownType(typeof(EmployeeInfo))]15 public interface IEmployeeContract16 {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.Service11 {12 public class EmployeeService : IEmployeeContract13 {14 #region IEmployeeContract Members15 16 public void SaveEmployee(EmployeeInfo employeeInfo)17 {18 try19 {20 EmployeeBLL bll = new EmployeeBLL();21 bll.SaveEmployee(employeeInfo);22 }23 catch24 {25 throw;26 }27 }28 29 public IList GetEmployees()30 {31 try32 {33 EmployeeBLL bll = new EmployeeBLL();34 return bll.GetEmployees();35 }36 catch37 {38 throw;39 }40 }41 42 #endregion43 }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的引用提供了可能。