65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (11投票s)

2010 年 5 月 18 日

CPOL

3分钟阅读

viewsIcon

50010

downloadIcon

876

基于 WCF 的服务总线架构 (2): 抽象 WCF。

引言

人们总是讨论如何从业务逻辑中抽象出一个 WCF 服务?我们如何最小化更改,将现有系统扩展为一个 WCF 服务?换句话说,我们总是希望开发应用程序时,不用担心将来可能需要开放的那些服务。当我们需要这些服务时,我们只需加载一个服务组件,并进行非常小的重构使其工作。我的设计是将订阅和抽象的函数服务封装到一个服务引擎中。在客户端,我设计一个端点来与服务引擎通信。

abstractservice.png

背景

我基于上一部分:如何订阅 WCF,将服务从业务逻辑中抽象出来。

使用代码

服务引擎

在我上一篇文章中,为了向客户端开放所有功能,我将 NewInventoryUpateInventory 等添加到 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
}

ContentContext 将存储需要发送的复杂信息。在 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);

注意:如果调试函数,您会遇到一个异常,即 InventorySAOTService 中未知。由于我们没有将 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));
© . All rights reserved.