如何将 WCF 服务抽象出业务逻辑






4.47/5 (11投票s)
基于 WCF 的服务总线架构 (2): 抽象 WCF。
引言
人们总是讨论如何从业务逻辑中抽象出一个 WCF 服务?我们如何最小化更改,将现有系统扩展为一个 WCF 服务?换句话说,我们总是希望开发应用程序时,不用担心将来可能需要开放的那些服务。当我们需要这些服务时,我们只需加载一个服务组件,并进行非常小的重构使其工作。我的设计是将订阅和抽象的函数服务封装到一个服务引擎中。在客户端,我设计一个端点来与服务引擎通信。
背景
我基于上一部分:如何订阅 WCF,将服务从业务逻辑中抽象出来。
使用代码
服务引擎
在我上一篇文章中,为了向客户端开放所有功能,我将 NewInventory
、UpateInventory
等添加到 InventoryService
中。这将阻止我们将服务从业务中抽象出来。我将要做的是编写一个名为 Invoke
的单一方法,为所有客户端调用提供服务。
public class SAOTService<T> : ISAOTService
{
public object Inovke(string methodname, params object[] args)
{
try
{
object obj = typeof(T).InvokeMember(methodname, BindingFlags.Static |
BindingFlags.Public | BindingFlags.IgnoreCase |
BindingFlags.InvokeMethod, null, null, args);
return obj;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
为了完全将服务从业务逻辑中抽象出来,我在服务实现类中设计了一个泛型 <T>
。 <T>
将接受一个类类型,提供真正的静态函数。在服务器应用程序上,如果您想将一些函数开放给 WCF 服务,请创建一个适配器类
public class ServiceAdapter
{
public static Inventory GetInventory(int productid)
{
return Inventory.GetInventory(productid);
}
public static Inventory[] GetInventoryList()
{
return Inventory.GetInventoryList();
}
public static bool ChangeInventory(string user, int productid, int changednumber)
{
Inventory inv = Inventory.GetInventory(productid);
return inv.ChangeInventory(user, changednumber);
}
public static int CreateNewProduct(Inventory inventory, string user)
{
return new Inventory(user,inventory.ProductName,inventory.CurrentInventory).ProductID;
}
}
然后,当您引用服务引擎时,将适配器发送到抽象的服务中。
ServiceController.OpenFunctionService<ServiceAdapter>(8886,"Inventory");
在客户端,打开与服务的连接
FunctionServiceConnector.CreateFunctionService<T>("Inventory",serviceaddress);
之后,您可以使用 Invoke
方法来获取 ServiceAdapter
函数。
FunctionServiceConnector.GetService("Inventory").Invoke("GetInventory", 1)
//get product inventory with id=1
对于订阅部分,为了避免已知的类型问题,我将订阅消息设计为一个固定的类 Message
[DataContract]
public class Message: EventArgs
{
#region Properties
[DataMember]
public string Title { get; set; }
[DataMember]
public string Content { get; set; }
[DataMember]
public string Context { get; set; }
[DataMember]
public DateTime Createtime { get; set; }
[DataMember]
public string PrimaryID { get; set; }
[DataMember]
public string Sender { get; set; }
[DataMember]
public string To { get; set; }
[DataMember]
public string CC { get; set; }
[DataMember]
public DateTime ExpiredTime { get; set; }
[DataMember]
public string Attachment { get; set; }
[DataMember]
public string FunctionCode { get; set; }
[DataMember]
public string ServiceName { get; set; }
#endregion
}
Content
和 Context
将存储需要发送的复杂信息。在 IMessageSenderCallBack
中有两个标准方法,为客户端提供所有订阅信息: void SendMessage(Message message); void SendHeartBeat(string address);
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IMessageSenderCallBack))]
public interface IMessageSender
{
[OperationContract(IsOneWay = false, IsInitiating = true,
IsTerminating = false)]
void Init(string address);
}
[ServiceContract]
public interface IMessageSenderCallBack
{
[OperationContract(IsOneWay = true)]
void SendMessage(Message message);
[OperationContract(IsOneWay = true)]
void SendHeartBeat(string address);
}
SendHeartBeat
持续向客户端发送心跳信息,以指示服务器仍在运行。SendMessage
将消息封装到客户端,指示客户端根据消息执行下一步逻辑。
完成的服务引擎有两个重要的方法,将被宿主应用程序调用
ServiceController.OpenFunctionService<T>(in portnumber,string servicename);
ServiceController.OpenMessageService(in portnumber, string servicename);
注意:如果调试函数,您会遇到一个异常,即 Inventory
在 SAOTService
中未知。由于我们没有将 Inventory
放入 SAOTService
,因此服务无法识别此类并正确序列化它。为了让序列化器知道此类型,我们在服务接口上添加了 KnownType
属性。
[ServiceKnownType("GetKnownTypes", typeof(KnowTypes))]
此语句告诉序列化器,类 KnowTypes
中的静态方法 GetKnownTypes
将提供一个类型列表,以便序列化器识别列表中的所有类型。
public static class KnowTypes
{
private static List<type> MyKnownTypes = new List<type>();
public static void AddKnownType(Type type)
{
MyKnownTypes.Add(type);
}
public static IEnumerable<type> GetKnownTypes(ICustomAttributeProvider provider)
{
return MyKnownTypes.ToArray();
}
}
然后,在我们打开服务之前,添加这些语句
ServiceController.AddKnowType(typeof(Inventory));
ServiceController.AddKnowType(typeof(Inventory[]));
记住,同样的异常也会发生在客户端代码中。
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory));
FunctionServiceConnector.AddKnownType(typeof(SAOT.Inventory[]));
服务端点
我创建了两个类来接收来自服务端点的函数服务和订阅服务。
//MessageDistributor
//get subscription service
class MessageDistributor : Trilobita.SAOT.Object.IMessageSenderCallBack
在 MessageDistributor
中,四个事件会告诉客户端应用程序订阅消息正在到来
//a subscription message arrives
public event EventHandler<Message> MessageDelivered;
// server connected
public event EventHandler
ServerConnectedEvent;
// server disconnected
public event EventHandler
ServerDisconnectedEvent;
// heart beat message from server every 2 seconds
public event EventHandler HeartBeatFromServer;
打开和关闭订阅服务
//open the subscription connection between client and server
MessageDistributor.CreateMessageService(servicename,address);
// close the subscription connection between client and server
MessageDistributor.CloseMessageService(servicename)
打开和关闭函数服务
FunctionServiceConnector.CreateFunctionService<T>(servicename, address);
FunctionServiceConnector.CloseFunctionService(servicename);
// get a function service proxy
FunctionServiceConnector.GetFunctionService<T>( servicename));