在上一篇讲到,如果将BLL层的每一个类都转化为*.svc,这是一个不实在的想法。它会使服务变化复杂,难于管理。
这时候,我们第一时间想到的是23个简单开发模式中的Factory,在这里,Factory正好派上用场。我们把这个Factory称之为管道(pipeline) ,通过这个管道客户端可以随意调用服务器BLL层里面的类。
(关于管道的概念,建议参考Cory Isaacson的杰作《多核应用架构关键技术—软件管道与soa》)
当你使用B/S方式开发UI层时,只要了解此开发模式,使用Ajax加上WCF里面的WebHttpBinding绑定和WebHttpBehavior行为,可以说是天衣无缝的组合。
首先,开发一个数据契约,其中包括程序集名称,类名,构造函数的参数,方法名,方法中的参数:
[DataContract]
public class Communication
{
[DataMember]
public string Assembly
{
get ;
set ;
}
[DataMember]
public string Class
{
get ;
set ;
}
[DataMember]
public object [] ConstructedParameters
{
get ;
set ;
}
[DataMember]
public string Method
{
get ;
set ;
}
[DataMember]
public object [] Parameters
{
get ;
set ;
}
}
public class Communication
{
[DataMember]
public string Assembly
{
get ;
set ;
}
[DataMember]
public string Class
{
get ;
set ;
}
[DataMember]
public object [] ConstructedParameters
{
get ;
set ;
}
[DataMember]
public string Method
{
get ;
set ;
}
[DataMember]
public object [] Parameters
{
get ;
set ;
}
}
为了证明客户端可以通过Ajax能够直接调用服务器WCF,我们先开发一个MyAssembly程序集:
namespace
MyAssembly
{
[DataContract]
public class User
{
[DataMember]
public int ID
{ get ; set ; }
[DataMember]
public string Name
{ get ; set ; }
[DataMember]
public int Age
{ get ; set ; }
}
public class UserManager
{
public List < User > GetList()
{
List < User > entities = new List < User > ();
User user = new User();
user.ID = 0 ;
user.Age = 26 ;
user.Name = " Leslie " ;
entities.Add(user);
return entities;
}
}
}
{
[DataContract]
public class User
{
[DataMember]
public int ID
{ get ; set ; }
[DataMember]
public string Name
{ get ; set ; }
[DataMember]
public int Age
{ get ; set ; }
}
public class UserManager
{
public List < User > GetList()
{
List < User > entities = new List < User > ();
User user = new User();
user.ID = 0 ;
user.Age = 26 ;
user.Name = " Leslie " ;
entities.Add(user);
return entities;
}
}
}
好,现在已经做好准备,现在我们新建一个“启动了AJAX的WCF服务”:
[ServiceContract(Namespace
=
"
myNamespace
"
)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
// 注意必须将RequirementsMode设置为AspNetCompatibilityRequirementsMode.Allowed
public class Service
{
private static Hashtable assemblies = new Hashtable();
private static Hashtable types = new Hashtable();
[OperationContract]
[WebGet]
// 要使用 HTTP GET,请添加 [WebGet] 特性。
public string DoWork(Communication communication)
{
Type classType = GetType(communication); // 通过自定义的GetType(Communicate o)方法加载类
if (classType != null ) // 下面将利用反射原理创建类对象
{
object reflectedObject;
if (communication.ConstructedParameters != null )
reflectedObject = Activator.CreateInstance(classType, communication.ConstructedParameters);
else
reflectedObject = Activator.CreateInstance(classType);
MethodInfo methodInfo = classType.GetMethod(communication.Method); // 获取方法信息
if (methodInfo != null )
{
object data = methodInfo.Invoke(reflectedObject, communication.Parameters); // 调用方法
if (data != null )
return Formate(data, methodInfo.ReturnType); // 将结果转化为JSON
else
return null ;
}
else
return null ;
}
return null ;
}
// 因为结果供于Ajax页面使用,所以将结果转化为Json形式
// 其实当项目已经启动AJAX,在默认情况下结果会自动转化为JSON,但因为不能事先预知返回的类型,所以把返回类型定为String
// 此处手动将结果转换成JSON字符串
public string Formate( object data,Type type)
{
using (Stream stream = new MemoryStream())
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(type);
jsonSerializer.WriteObject(stream, data);
byte [] byteData = new byte [stream.Length];
stream.Seek( 0 , 0 );
stream.Read(byteData, 0 , ( int )stream.Length);
stream.Close();
return Encoding.UTF8.GetString(byteData);
}
}
// 加载程序集
private Assembly GetAssembly(Communication communication)
{
if ( ! assemblies.ContainsKey(communication.Assembly))
{
Assembly myAssembly = Assembly.Load(communication.Assembly);
assemblies.Add(communication.Assembly, myAssembly);
}
return (Assembly)assemblies[communication.Assembly];
}
// 加载类
private Type GetType(Communication communication)
{
if ( ! types.ContainsKey(communication.Class))
{
Assembly assembly = GetAssembly(communication);
types.Add(communication.Class, assembly.GetType(communication.Class));
}
return (Type)types[communication.Class];
}
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
// 注意必须将RequirementsMode设置为AspNetCompatibilityRequirementsMode.Allowed
public class Service
{
private static Hashtable assemblies = new Hashtable();
private static Hashtable types = new Hashtable();
[OperationContract]
[WebGet]
// 要使用 HTTP GET,请添加 [WebGet] 特性。
public string DoWork(Communication communication)
{
Type classType = GetType(communication); // 通过自定义的GetType(Communicate o)方法加载类
if (classType != null ) // 下面将利用反射原理创建类对象
{
object reflectedObject;
if (communication.ConstructedParameters != null )
reflectedObject = Activator.CreateInstance(classType, communication.ConstructedParameters);
else
reflectedObject = Activator.CreateInstance(classType);
MethodInfo methodInfo = classType.GetMethod(communication.Method); // 获取方法信息
if (methodInfo != null )
{
object data = methodInfo.Invoke(reflectedObject, communication.Parameters); // 调用方法
if (data != null )
return Formate(data, methodInfo.ReturnType); // 将结果转化为JSON
else
return null ;
}
else
return null ;
}
return null ;
}
// 因为结果供于Ajax页面使用,所以将结果转化为Json形式
// 其实当项目已经启动AJAX,在默认情况下结果会自动转化为JSON,但因为不能事先预知返回的类型,所以把返回类型定为String
// 此处手动将结果转换成JSON字符串
public string Formate( object data,Type type)
{
using (Stream stream = new MemoryStream())
{
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(type);
jsonSerializer.WriteObject(stream, data);
byte [] byteData = new byte [stream.Length];
stream.Seek( 0 , 0 );
stream.Read(byteData, 0 , ( int )stream.Length);
stream.Close();
return Encoding.UTF8.GetString(byteData);
}
}
// 加载程序集
private Assembly GetAssembly(Communication communication)
{
if ( ! assemblies.ContainsKey(communication.Assembly))
{
Assembly myAssembly = Assembly.Load(communication.Assembly);
assemblies.Add(communication.Assembly, myAssembly);
}
return (Assembly)assemblies[communication.Assembly];
}
// 加载类
private Type GetType(Communication communication)
{
if ( ! types.ContainsKey(communication.Class))
{
Assembly assembly = GetAssembly(communication);
types.Add(communication.Class, assembly.GetType(communication.Class));
}
return (Type)types[communication.Class];
}
}
服务器端会自动为你配置.config文件:
<
system.serviceModel
>
< serviceHostingEnvironment aspNetCompatibilityEnabled ="true" />
< behaviors >
< endpointBehaviors >
< behavior name ="ServiceAspNetAjaxBehavior" >
< enableWebScript /> //注意启动enableWebScript
</ behavior >
</ endpointBehaviors >
< serviceBehaviors >
< behavior name ="ServiceBehavior" >
< serviceMetadata httpGetEnabled ="true" /> //注意此处启动了httpGetEnabled
< serviceDebug includeExceptionDetailInFaults ="false" />
</ behavior >
</ serviceBehaviors >
</ behaviors >
< services >
< service name ="Service" behaviorConfiguration ="ServiceBehavior" >
< endpoint address ="" behaviorConfiguration ="ServiceAspNetAjaxBehavior"
binding ="webHttpBinding " contract ="Service" /> //注意绑定的是webHttpBinding
< endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />
</ service >
</ services >
</ system.serviceModel >
</ configuration >
< serviceHostingEnvironment aspNetCompatibilityEnabled ="true" />
< behaviors >
< endpointBehaviors >
< behavior name ="ServiceAspNetAjaxBehavior" >
< enableWebScript /> //注意启动enableWebScript
</ behavior >
</ endpointBehaviors >
< serviceBehaviors >
< behavior name ="ServiceBehavior" >
< serviceMetadata httpGetEnabled ="true" /> //注意此处启动了httpGetEnabled
< serviceDebug includeExceptionDetailInFaults ="false" />
</ behavior >
</ serviceBehaviors >
</ behaviors >
< services >
< service name ="Service" behaviorConfiguration ="ServiceBehavior" >
< endpoint address ="" behaviorConfiguration ="ServiceAspNetAjaxBehavior"
binding ="webHttpBinding " contract ="Service" /> //注意绑定的是webHttpBinding
< endpoint address ="mex" binding ="mexHttpBinding" contract ="IMetadataExchange" />
</ service >
</ services >
</ system.serviceModel >
</ configuration >
好吧,现在万事俱备的时候,让我们开发一个测试页面:
<
body
>
< form id ="form1" runat ="server" >
< script type ="text/javascript" >
window.onload = function () {
myNamespace.Service.set_path( " http://localhost:8080/Service.svc/ " );
var communication = { " Assembly " : " MyAssembly " , " Class " : " MyAssembly.UserManager " ,
" ConstructedParameters " : null , " Method " : " GetList " , " Parameters " : null };
// 把Communication参数转化为Json形式
myNamespace.Service.DoWork(communication, OnSucceed, OnFail, null );
}
function OnSucceed(result) {
if (result != null )
alert(result);
}
function OnFail(result) {
alert(result);
}
</ script >
</ form >
</ body >
< form id ="form1" runat ="server" >
< script type ="text/javascript" >
window.onload = function () {
myNamespace.Service.set_path( " http://localhost:8080/Service.svc/ " );
var communication = { " Assembly " : " MyAssembly " , " Class " : " MyAssembly.UserManager " ,
" ConstructedParameters " : null , " Method " : " GetList " , " Parameters " : null };
// 把Communication参数转化为Json形式
myNamespace.Service.DoWork(communication, OnSucceed, OnFail, null );
}
function OnSucceed(result) {
if (result != null )
alert(result);
}
function OnFail(result) {
alert(result);
}
</ script >
</ form >
</ body >
测试成功:
恭喜你终于学会如何使用Ajax+WCF进行页面数据显示了。
你应该初步了解到如何使用管道Pipeline进行客户端与服务器端的通讯,自此之后,每逢你进行简单的页面开发时都可使用此方式。好处在于页面无 需了解数据是从何处获取的,因为数据存取和页面可以处于不同的线程池,所以这样做可以把服务器压力降到最低。同时你可以使用异步的服务,来进一步提高数据 站点的线程池效率。(异步服务可参考ASP.NET服务器端多线程设计 )