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

远程操作层

starIconstarIconstarIconstarIconstarIcon

5.00/5 (17投票s)

2014 年 4 月 11 日

CPOL

9分钟阅读

viewsIcon

21611

downloadIcon

337

服务层,它隐藏了具体的DCOM技术细节,并且在实现新功能时无需更改。

引言

本文展示了我们如何创建一个服务层,它能够与远程端通信,可以与不同的远程处理技术协同工作,隐藏了技术细节,并且在实现新功能时无需更改。这对我们有什么好处?

隐藏具体的技术是很有用的,因为在项目期间有很多原因需要更改它。例如,我们曾经在 Windows Communication Foundation 中遇到性能问题,并决定编写我们自己的套接字通信。通过这种架构,这样的更改可以很容易地完成,而无需更改其他组件中的任何内容。

服务契约描述了公开的、可以从远程端调用的方法。契约可以是包含方法的接口,也可以是 WSDL 文件。当新功能添加到项目中时,服务契约中会出现新方法。我们希望避免这些更改,并创建一个在项目生命周期内不会更改的服务契约。

我们将展示该架构的具体用法,其中通信技术是 WCF。

背景

我们的服务架构使用类似于 `WCF` 约定的方法、事件和状态名称。因此,熟悉 .NET ServiceModel 类库很有用,尤其是为了理解具体的 `WCF` 实现。

使用代码

我们定义接口和类来封装所有通信功能。这样,我们隐藏了当前远程处理技术的所有具体细节。高级架构显示,在两端,业务层都使用远程操作层来在远程端发起操作。两个远程操作层使用配置的技术相互通信。

远程操作描述符

`RemoteOperationDescriptor` 类表示通过网络发送的远程操作,包含关于远程端应执行什么的信息。远程操作可以通过三个属性来描述:接口类型、在该接口上调用的方法名称以及该方法的参数。基于远程端的接口类型,从 DI 容器中检索接口的实现。在此实例上,可以使用给定参数调用给定方法。

[Serializable]
public class RemoteOperationDescriptor
{
    public RemoteOperationDescriptor(string interfaceType, string methodName, params object[] paramters)
    {
        if (interfaceType == null)
        {
            throw new ArgumentNullException("interfaceType");
        }

        if (methodName == null)
        {
            throw new ArgumentNullException("methodName");
        }

        InterfaceType = interfaceType;
        MethodName = methodName;
        Parameters = paramters;
    }

    public string InterfaceType
    {
        get;
        set;
    }
        
    public string MethodName
    {
        get;
        set;
    }
    public object[] Parameters
    {
        get;
        set;
    }
}  

自定义属性

出于安全原因,远程可调用接口和方法都用属性标记。`RemoteCallableTypeAttribute` 表示它们可以远程访问。类似地,`RemoteCallableFuncAttribute` 属性表示可以从远程端调用的方法。在每次执行之前,会检查 `RemoteOperationDescriptor` 中给定的接口和方法是否标记了这些属性。如果没有,则抛出异常。

[AttributeUsage(AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
public class RemoteCallableTypeAttribute : Attribute
{
    public bool RegisterInDIContainer { get; set; }

    public RemoteCallableTypeAttribute() : this(false)
    {
    }

    public RemoteCallableTypeAttribute(bool registerInDIContainer)
    {
        this.RegisterInDIContainer = registerInDIContainer;
    }
} 
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class RemoteCallableFuncAttribute : Attribute
{
}  

请求和响应类

`RemoteOperationDescriptor` 类被封装在一个请求对象中。请求对象是一个 XML 可序列化对象,它被发送到远程端。这个请求对象负责以 XML 格式序列化 `RemoteOperationDescriptor`。我们还有一个 XML 可序列化响应对象,它只包含一个 `object` 类型的返回值。我们为请求和响应定义了一个公共基类,它包含一个标识符。标识符的类型是 `RemoteSideIDType`,它是一个唯一值,用于标识调用者。您可以在下面看到类层次结构。

[Serializable]
public abstract class RemoteRequestResponseBase : IXmlSerializable, SocketCommunication.ISocketXmlSerializable
{
    public XmlSchema GetSchema()
    {
        return null;
    }

    public RemoteSideIDType RemoteID = null;

    #region IXmlSerializable Members
    ...
    #endregion


    #region ISocketXmlSerializable Members
    ...
    #endregion
} 
[Serializable]
public class RemoteRequest : RemoteRequestBase
{
    internal RemoteOperationDescriptor ExecuteOnRemoteSideOperation = null;

    #region IXmlSerializable Members
    ....
    #endregion
} 
[Serializable]
public class RemoteResponse : RemoteResponseBase
{
    public object ReturnValue;

    #region IXmlSerializable Members
    ....
    #endregion
} 

发起远程调用

`IRemoteSideCommunicationContract` 接口描述了远程方之间的契约。它只有一个方法,名为 `ExecuteOnRemoteSide`,它接受一个 `RemoteRequest` 参数并返回一个 `RemoteResponse`。如果使用新的远程处理库,则应实现此接口。例如,我们有两个默认实现,一个用于 WCF,一个用于我们自己的基于套接字的通信框架。

 /// <summary>
/// Interface which contains the remote callable methods
/// </summary>
[ServiceContract(CallbackContract=typeof(IRemoteSideCommunicationContract))]
public interface IRemoteSideCommunicationContract
{
    /// <summary>
    /// Remote callable method which handles all the requests from the remote side
    /// </summary>
    [OperationContract]
    RemoteResponse ExecuteRequest(RemoteRequest request);
}  

`RemoteOperationHandler` 是一个帮助类,它帮助我们从 `RemoteOperationDescriptor` 构造一个新的 `RemoteRequest` 对象。它还包含如何在新到达的 `RemoteRequest` 的 `ExecuteRequest` 方法中处理和执行的逻辑。首先,它搜索请求中描述的接口类型和方法。然后,它检查接口和方法的属性,如果它们是远程可调用的,则使用请求中给定的参数调用该方法。调用后,它将返回值封装在 `RemoteResponse` 对象中。如果在方法调用中发生异常,它会捕获异常并将异常对象放入返回值属性中。`RemoteOperationHandler` 有一个处理 `RemoteResponse` 对象的方法。该方法检查响应对象值并返回它。如果该方法在返回值中找到异常,则抛出该异常。这是此服务层的一个限制:远程操作不能将 Exception 作为返回类型,因为在调用方无法判断它是返回值还是抛出的异常。

/// <summary>
/// Interface which describe methods to generate request from RemoteSideOperation and handle responses.
/// </summary>
public interface IRemoteOperationHandler
{
    /// <summary>
    /// Creates the request object from the RemoteSideOperation
    /// </summary>
    /// <param name="rso">Describes the request parameters</param>
    /// <returns>Created request object</returns>
    RemoteRequest CreateRequest(RemoteOperationDescriptor rso);

    /// <summary>
    /// Executes the request on the remote side.
    /// </summary>
    /// <param name="request">Request</param>
    /// <param name="callableTypeAttribute"></param>
    /// <param name="callableFuncAttribute"></param>
    /// <returns>Response</returns>
    RemoteResponse ExecuteRequest(RemoteRequest request, Type callableTypeAttribute, Type callableFuncAttribute);

    /// <summary>
    /// Handles the response of the remote side request
    /// </summary>
    /// <param name="resp">Response object</param>
    /// <returns>Value stored in response</returns>
    object HandleResponse(RemoteResponse resp);
}  

`IRemoteSideCommunicationHandler` 定义了一个契约,用于发起对远程端的调用并处理连接。该接口定义了以下成员

两个 `ExecuteOnRemoteSide` 方法负责发起新请求。这两个方法都获取一个 `RemoteOperationDescriptor`,其中包含调用的详细信息,以及一个 `RemoteSideIDType`,用于标识哪个远程端获取此请求。

`CurrentRemoteSideID` 返回本地端的标识符。

当新的远程端连接或断开连接时,会触发 `RemoteSideConnected` 和 `RemoteSideDisconnected` 事件。

`AssignRemoteSide` 方法用于将当前的 `IRemoteSide` 对象分配给通信处理程序。`IRemoteSide` 接口表示通信的端点,并定义抽象通信端点的方法。

/// <summary>
/// Describes a communicator object to start remote calls and handles states
/// </summary>
public interface IRemoteSideCommunicationHandler
{
    /// <summary>
    /// Executes a request on the remote side and gives the return back
    /// </summary>
    /// <typeparam name="TResult">Return type of the remote call</typeparam>
    /// <param name="remoteSideID">Remote client ID</param>
    /// <param name="rso">The descriptor of the operation</param>
    /// <returns>Result</returns>
    TResult ExecuteOnRemoteSide<TResult>(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso);

    /// <summary>
    /// Executes a request on the remote side
    /// </summary>
    /// <param name="remoteSideID">Remote client ID</param>
    /// <param name="rso">The descriptor of the operation</param>
    void ExecuteOnRemoteSide(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso);

    /// <summary>
    /// The id of the remote client whos request is executed currently
    /// </summary>
    RemoteSideIDType CurrentRemoteSideID { get; }

    /// <summary>
    /// Remote client connected event
    /// </summary>
    event EventHandler<RemoteSideConnectedEventArgs> RemoteSideConnected;

    /// <summary>
    /// Remote client disconnected event
    /// </summary>
    event EventHandler<RemoteSideDisconnectedEventArgs> RemoteSideDisconnected;

    /// <summary>
    /// Assign the concrete ClientServiceHost
    /// </summary>
    /// <param name="remoteSide">Service host</param>
    void AssignRemoteSide(IRemoteSide remoteSide);
}  

`IRemoteSide` 接口定义了用于创建和关闭通信的方法(`Open`、`Close`、`Abort`)以及这些方法的事件(`Opened`、`Closed`)。它还包含状态信息和状态更改事件。最后,它可以返回用于发起远程调用的契约对象(`GetCurrentRemoteSideCommunicationContract`)。

/// <summary>
/// Represent a remote side communication endpoint
/// </summary>
public interface IRemoteSide
{
    /// <summary>
    /// Raised after the communication is closed
    /// </summary>
    event EventHandler Closed;
    /// <summary>
    /// Raised after the communication is faulted
    /// </summary>
    event EventHandler Faulted;
    /// <summary>
    /// State of the communication
    /// </summary>
    RemoteCommunicationState State { get; }
    /// <summary>
    /// Raised after the state changed
    /// </summary>
    event EventHandler StateChanged;
    /// <summary>
    /// Open the communication
    /// </summary>
    void Open();
    /// <summary>
    /// Close the communication 
    /// </summary>
    void Close();
    /// <summary>
    /// Abort the communication
    /// </summary>
    void Abort();
    /// <summary>
    /// Concrete communication contract implementation
    /// </summary>
    /// <returns></returns>
    IRemoteSideCommunicationContract GetCurrentRemoteSideCommunicationContract();
}  

为了实例化一个 `IRemoteSide` 接口实现,我们使用 `IRemoteSideFactory` 接口中定义的工厂模式。

/// <summary>
/// Factory which creates a new IRemoteSide
/// </summary>
public interface IRemoteSideFactory
{
    /// <summary>
    /// Creates a new remote side instance
    /// </summary>
    /// <returns>New instance</returns>
    IRemoteSide CreateInstance();
}   

现在让我们看看这些接口的实现。`RemoteSideCommunicator` 类实现了 `IRemoteSide` 接口和 `IRemoteSideCommunicationContract` 接口。因此,这个类负责与远程端的所有通信。它处理并执行来自远程端的调用,并向远程端发起新的调用。构造函数获取一个 `IRemoteOperationHandler` 实现来处理传入的调用。一个通信器可以处理多个由 `RemoteSideID` 区分的远程端。

该类还有一些事件,用于在远程端连接或已知远程端断开连接时触发。这是一个基类,提供了一些虚拟方法供重写。通过这些方法,我们可以修改或封装所有远程调用。例如,我们使用有关客户端的会话信息,并使用这些虚拟方法在通信期间创建、管理和删除会话。

 public class RemoteSideCommunicator : IRemoteSideCommunicationHandler, IRemoteSideCommunicationContract
{
    protected IRemoteOperationHandler remoteOperationHandler = null;
    protected IRemoteSide remoteSide = null;

    public RemoteSideCommunicator(IRemoteOperationHandler remoteOperationHandler)
    {
    ....
    }

    public RemoteSideCommunicator(IRemoteOperationHandler remoteOperationHandler, Tuple<RemoteSideIDType, IRemoteSideCommunicationContract> alreadyKnownRemoteSide) :this(remoteOperationHandler)
    {
    ....
    }

    public RemoteSideIDType CurrentRemoteSideID
    {
        get
        {
        ....
        }
    }

    protected virtual void NewRemoteSideConnected(RemoteSideIDType remoteSideID)
    {
    }

    protected virtual void KnownRemoteSideRequest(RemoteSideIDType remoteSideID)
    {
    }

    protected virtual void RemoteSideReconnected(RemoteSideIDType remoteSideID)
    {
    }

    protected virtual void RemoteRequestStarted(RemoteSideIDType remoteSideID, RemoteRequest request)
    {
    }

    protected virtual void RemoteRequestFinished(RemoteSideIDType remoteSideID, RemoteRequest request)
    {
    }

    public virtual RemoteResponse ExecuteRequest(RemoteRequest request)
    {
        ....
        IRemoteSideCommunicationContract currentContract = wrapper.GetCurrentRemoteSideCommunicationContract();

        try
        {
            try
            {
                ....
                RemoteRequestStarted(remoteID, request);

                if (communicationContractsByRemoteSideIDDict.TryGetValue(remoteID, out lastKnownContract))
                {
                    ....
                    KnownRemoteSideRequest(remoteID);
                }
                else
                {
                    ....
                    NewRemoteSideConnected(remoteID);
                }
            }
            finally
            {
            }
            ....
            if (isNewRemoteSide)
            {
                OnRemoteSideConnected(remoteID);
            }
            ret = remoteOperationHandler.ExecuteRequest(request, typeof(RemoteCallableTypeAttribute), typeof(RemoteCallableFuncAttribute));
        }
        finally
        {
            RemoteRequestFinished(remoteID, request);
        }
        ....        
    }

    protected virtual void RemoteSideFaulted(RemoteSideIDType remoteID)
    {
    }

    private void Channel_Faulted(object sender, EventArgs e)
    {
        ....
        RemoteSideFaulted(remoteSideID);
        ....
    }


    public event EventHandler<RemoteSideConnectedEventArgs> RemoteSideConnected;
    public event EventHandler<RemoteSideDisconnectedEventArgs> RemoteSideDisconnected;

    public TResult ExecuteOnRemoteSide<TResult>(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso)
    {
        ....
        TResult ret = (TResult)ExecuteOnRemoteSideInternal(remoteSideID, rso);
        ....
    }

    public void ExecuteOnRemoteSide(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso)
    {
        ....
        ExecuteOnRemoteSideInternal(remoteSideID, rso);
    }

    protected virtual void RequestToRemoteSideStarted(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso)
    {
    }

    protected virtual void RequestToRemoteSideFinished(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso)
    {
    }

    protected virtual object ExecuteOnRemoteSideInternal(RemoteSideIDType remoteSideID, RemoteOperationDescriptor rso)
    {
        ....
        RemoteRequest req = remoteOperationHandler.CreateRequest(rso);
        req.RemoteID = remoteSideID;

        RemoteResponse resp = null;
        try
        {
            RequestToRemoteSideStarted(remoteSideID, rso);
            resp = contract.ExecuteRequest(req);
        }
        finally
        {
            RequestToRemoteSideFinished(remoteSideID, rso);
        }
        ret = remoteOperationHandler.HandleResponse(resp);
        ....
    }
}    

WCF 实现

任何您希望与此架构一起使用的通信技术都需要实现以下接口:`IRemoteSide`、`IRemoteSideCommunicationContract` 和 `IRemoteSideFactory`。例如,在我们的项目中,我们实现了两个不同的通信层:一个基于 WCF,另一个是我们自己的基于套接字的通信框架。在本节中,我们将向您介绍基于 WCF 的实现。

在此实现中,我们创建了一个服务器端和客户端 WCF 组件。对于服务器端,我们在继承自 System.ServiceModel.ServiceHost 的 `WCFServiceHost` 类中实现 `IRemoteSide` 和 `IRemoteSideCommunicationContract` 接口。`ServiceHost` 包含用于打开和关闭通信的方法和事件。我们只需要实现状态处理和返回契约实例。

public class WCFServiceHost : ServiceHost, IRemoteSide, IRemoteSideCommunicationContract
{
    private IWCFConfigManager cm = null;
    private IDIContainer diContainer = null;
    private IRemoteSideCommunicationContract clientServiceContractInstance = null;

    public WCFServiceHost(IDIContainer diContainer, IRemoteSideCommunicationContract clientServiceContractInstance, Uri clientServiceAddress)
        : base(clientServiceContractInstance, clientServiceAddress)
    {
        this.diContainer = diContainer;
        this.clientServiceContractInstance = clientServiceContractInstance;
        cm = diContainer.GetLazyBoundInstance<IWCFConfigManager>();

        ApplyConfiguration();

        this.AddServiceEndpoint(
            ServiceMetadataBehavior.MexContractName, 
            MetadataExchangeBindings.CreateMexTcpBinding(),
            String.Concat(new Uri(cm.Value.ClientServiceAddress).OriginalString, "/mex"));

        this.Closed += new EventHandler(clientServiceHost_StateChanged);
        this.Closing += new EventHandler(clientServiceHost_StateChanged);
        this.Faulted += new EventHandler(clientServiceHost_StateChanged);
        this.Opened += new EventHandler(clientServiceHost_StateChanged);
        this.Opening += new EventHandler(clientServiceHost_StateChanged);
    }

    private void clientServiceHost_StateChanged(object sender, EventArgs e)
    {
        Log.Debug("ClientServiceHost.State changed: {0}", this.State.ToString());
        OnStateChanged();
    }

    private Binding CreateBinding()
    {
        NetTcpBinding binding = new NetTcpBinding();

        WCFHelper.ApplyWCFBindingLimits(
            binding,
            cm.Value.ClientServiceMaxSizeInBytes,
            cm.Value.ClientServiceTimeoutInSecs);
        binding.Security.Mode = SecurityMode.None;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;

        return binding;
    }

    protected override void ApplyConfiguration()
    {
        if (cm != null)
        {
            ServiceDebugBehavior debug = new ServiceDebugBehavior();
            debug.IncludeExceptionDetailInFaults = true;
            this.Description.Behaviors.Add(debug);

            ServiceThrottlingBehavior throttling = new ServiceThrottlingBehavior();
            throttling.MaxConcurrentCalls = cm.Value.ClientServiceMaxConcurrentCalls;
            throttling.MaxConcurrentInstances = cm.Value.ClientServiceMaxConcurrentInstances;
            throttling.MaxConcurrentSessions = cm.Value.ClientServiceMaxConcurrentSessions;
            this.Description.Behaviors.Add(throttling);

            Binding binding = CreateBinding();
            ServiceEndpoint ep = this.AddServiceEndpoint(typeof(IRemoteSideCommunicationContract), binding, String.Empty);
            EndpointAddress epa = new EndpointAddress(ep.Address.Uri, EndpointIdentity.CreateDnsIdentity("localhost"), ep.Address.Headers);
            ep.Address = epa;

            WCFHelper.ApplyWCFEndpointLimits(ep, cm.Value.ClientServiceMaxItemsInObjectGraph);

            ServiceMetadataBehavior mb = new ServiceMetadataBehavior();
            this.Description.Behaviors.Add(mb);

            ServiceBehaviorAttribute sba = (ServiceBehaviorAttribute)(from a in this.Description.Behaviors
                                                                        where a.GetType() == typeof(ServiceBehaviorAttribute)
                                                                        select a).Single();

            sba.InstanceContextMode = InstanceContextMode.Single;
            sba.ConcurrencyMode = ConcurrencyMode.Multiple;
            sba.UseSynchronizationContext = false;
        }
    }

    public IRemoteSideCommunicationContract GetCurrentRemoteSideCommunicationContract()
    {
        IRemoteSideCommunicationContract ret = null;

        var cc = OperationContext.Current;
        if (cc != null)
        {
            ret = cc.GetCallbackChannel<IRemoteSideCommunicationContract>();
        }

        return ret;
    }

    public new RemoteCommunicationState State
    {
        get;
        private set;
    }

    public event EventHandler StateChanged;

    private void OnStateChanged()
    {
        if (this.StateChanged != null)
        {
            StateChanged(this, EventArgs.Empty);
        }
    }

    public RemoteResponse ExecuteRequest(RemoteRequest request)
    {
        RemoteResponse ret;
        ret = clientServiceContractInstance.ExecuteRequest(request);
        return ret;
    }
} 

`IRemoteSideFactory` 在 `WCFServiceHostFactory` 类中实现。这个类可以创建 `WCFServiceHost` 的新实例并将其分配给当前通信器。

public class WCFServiceHostFactory : IRemoteSideFactory
{
    private IDIContainer diContainer = null;
    public WCFServiceHostFactory(IDIContainer diContainer)
    {
        this.diContainer = diContainer;
    }

    public IRemoteSide CreateInstance()
    {
        IRemoteSide ret = null;

        var cm = diContainer.GetLazyBoundInstance<IWCFConfigManager>().Value;
        var clientServiceInstance = diContainer.GetLazyBoundInstance<IRemoteSideCommunicationHandler>().Value;
        ret = new WCFServiceHost(diContainer, (IRemoteSideCommunicationContract)clientServiceInstance, new Uri(cm.ClientServiceAddress));
        clientServiceInstance.AssignRemoteSide(ret);

        return ret;
    }
}  

这两个类都使用一个 `IWCFConfigManager` 接口,它描述了 WCF 通信通道的属性。此接口没有默认实现,因为它取决于我们使用此库的应用程序。如果您有配置文件,您应该创建一个实现此接口的类,并从配置文件或配置管理器中检索具体的值。现在我们不在此处定义 WCF 通道的所有属性,只定义我们真正希望允许从配置文件中更改的属性。如果需要,扩展此接口以添加更多属性非常容易。

public interface IWCFConfigManager
{
    string ClientServiceAddress { get; }
    int ClientServiceMaxItemsInObjectGraph { get; }
    int ClientServiceMaxSizeInBytes { get; }
    int ClientServiceMaxConcurrentCalls { get; }
    int ClientServiceMaxConcurrentInstances { get; }
    int ClientServiceMaxConcurrentSessions { get; }
    int ClientServiceTimeoutInSecs { get; }
}   

例如,我们将这些设置存储在配置文件中,并且配置文件值可以通过配置管理器访问。

internal class WCFConfigManager : IWCFConfigManager
{
    private IConfigManager configManagerInstance = DIContainer.Instance.GetLazyBoundInstance<IConfigManager>();

    private int? clientServiceMaxItemsInObjectGraph = null;
    public int ClientServiceMaxItemsInObjectGraph
    {
        get
        {
            if (!clientServiceMaxItemsInObjectGraph.HasValue)
            {
                clientServiceMaxItemsInObjectGraph = configManagerInstance.Value.GetValue<int?>("WCFClientServiceMaxItemsInObjectGraph", true, 65535);
            }
            return clientServiceMaxItemsInObjectGraph.Value;
        }
    }

    ....
}

WCF 客户端实现继承自 `DuplexClientBase` 泛型类。通道类型是我们的通信契约接口 `IRemoteSideCommunicationContract`。此客户端还实现了 `IRemoteSide` 和 `IRemoteSideCommunicationContract` 接口。因此,它可以发起远程调用并处理来自远程端的调用。它使用 `base.Channel` 向服务器端发送请求。

public class WCFServiceClient : DuplexClientBase<IRemoteSideCommunicationContract>, IRemoteSide, IRemoteSideCommunicationContract
{
    public WCFServiceClient(InstanceContext instanceContext, Binding binding, EndpointAddress remoteAddress) :
        base(instanceContext, binding, remoteAddress)
    {
        ((ICommunicationObject)base.Channel).Closed += new EventHandler(ClientServiceContractClient_StateChanged);
        ((ICommunicationObject)base.Channel).Closing += new EventHandler(ClientServiceContractClient_StateChanged);
        ((ICommunicationObject)base.Channel).Faulted += new EventHandler(ClientServiceContractClient_StateChanged);
        ((ICommunicationObject)base.Channel).Opened += new EventHandler(ClientServiceContractClient_StateChanged);
        ((ICommunicationObject)base.Channel).Opening += new EventHandler(ClientServiceContractClient_StateChanged);
    }

    public event EventHandler StateChanged;

    public new RemoteCommunicationState State
    {
        get
        {
            RemoteCommunicationState ret = (RemoteCommunicationState)Enum.Parse(typeof(RemoteCommunicationState), base.State.ToString());
            return ret;
        }
    }

    private void OnStateChanged()
    {
        if (this.StateChanged != null)
        {
            StateChanged(this, EventArgs.Empty);
        }
    }

    private void ClientServiceContractClient_StateChanged(object sender, EventArgs e)
    {
        Log.Debug("WCFServiceClient.State changed: {0}", ((ICommunicationObject)base.Channel).State.ToString());
        OnStateChanged();
    }


    public RemoteResponse ExecuteRequest(RemoteRequest oRequest)
    {
        RemoteResponse ret = null;
        try
        {
            try
            {
                ret = base.Channel.ExecuteRequest(oRequest);
            }
            catch (FaultException<ExceptionDetail> ex)
            {
                Log.Error(ex);

                // extract & throw original exception from Fault contract
                Exception originalException = null;
                try
                {
                    originalException = (Exception)Activator.CreateInstance(Type.GetType(ex.Detail.Type), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new object[] { ex.Message, ex }, null);
                }
                catch
                {
                    ExceptionHelper.RethrowExceptionPreservingStackTrace(ex);
                }
                throw originalException;
            }
        }
        catch (CommunicationObjectFaultedException ex)
        {
            // wrap WCF specific exception
            throw new RemoteSideFaultedException(ex.Message, ex);
        }
        catch (EndpointNotFoundException ex)
        {
            // wrap WCF specific exception
            throw new RemoteSideUnreachableException(ex.Message, ex);
        }
        return ret;
    }


    public event EventHandler Closed
    {
        add
        {
            ((ICommunicationObject)this).Closed += value;
        }
        remove 
        {
            ((ICommunicationObject)this).Closed -= value;
        }
    }

    public event EventHandler Faulted
    {
        add
        {
            ((ICommunicationObject)this).Faulted += value;
        }
        remove
        {
            ((ICommunicationObject)this).Faulted -= value;
        }
    }        
    public IRemoteSideCommunicationContract GetCurrentRemoteSideCommunicationContract()
    {
        IRemoteSideCommunicationContract ret = this;
        return ret;
    }
}  

客户端也有自己的工厂实现,`WCFServiceClientFactory`。

public class WCFServiceClientFactory : IRemoteSideFactory
{
    private IDIContainer diContainer = null;
    public WCFServiceClientFactory(IDIContainer diContainer)
    {
        this.diContainer = diContainer;
    }

    public IRemoteSide CreateInstance()
    {
        IRemoteSide ret = null;

        // init binding
        var binding = new NetTcpBinding();
        binding.Security.Mode = SecurityMode.None;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;

        var cm = diContainer.GetLazyBoundInstance<IWCFConfigManager>().Value;
        WCFHelper.ApplyWCFBindingLimits(
            binding,
            cm.ClientServiceMaxSizeInBytes,
            cm.ClientServiceTimeoutInSecs);

        // init endpoint address
        var endpointAddress = new EndpointAddress(cm.ClientServiceAddress);

        // create context
        var clientServiceInstance = diContainer.GetLazyBoundInstance<IRemoteSideCommunicationHandler>().Value;
        var instanceContext = new InstanceContext((IRemoteSideCommunicationContract)clientServiceInstance);

        // create client
        var cscc = new WCFServiceClient(instanceContext, binding, endpointAddress);
        clientServiceInstance.AssignRemoteSide(cscc);

        // init client
        WCFHelper.ApplyWCFEndpointLimits(cscc.Endpoint, cm.ClientServiceMaxItemsInObjectGraph);

        ret = cscc;

        return ret;
    }
}  

此图总结了主要的接口和类。

使用类库

现在让我们看看这个库如何在一个项目中作为服务层使用。当项目使用 WCF 作为通信层时,唯一的任务是在 DI 容器中注册适当的类。在服务器端,您需要将 `WCFServiceHostFactory` 类注册为 `IRemoteSideFactory` 实现。您需要实现 `WCFConfigManager`,它从应用程序配置存储中检索配置参数。`RemoteSideCommunicator` 处理来自客户端的请求,因此您需要在 DI 容器中注册此类的实例,作为 `IRemoteSideCommunicationHandler` 实现和 `IRemoteSideCommunicationContract` 实现。此时,您可以使用工厂创建一个新的 `IRemoteSide` 实例并打开通信。由于工厂将创建的 `IRemoteSide` 分配给 `RemoteSideCommunicator`,现在它已准备好在应用程序中的任何地方用于发起新的远程调用。

您需要在客户端执行完全相同的操作,但要将 `WCFServiceClientFactory` 注册为 `IRemoteSideFactory`。

序列图显示了客户端如何初始化新的通信对象,以及发起远程调用时会发生什么。

关注点

这种架构提供了一种解决方案,用于创建一个完全封装通信服务的服务层。当项目生命周期中需要替换通信技术时,这种方法变得极其方便。如果您对通信层有特殊或不同的要求,您只需几行代码即可轻松地引入新的实现,而不会对应用程序的其他层产生任何影响。

我将向您展示一个具体的示例,说明我们如何结合我们的动态代理生成器和此服务层来创建一个完全隐藏服务层的架构,这样您甚至不需要担心是本地调用函数还是远程调用函数

历史

  • 2014年4月14日:初始版本

© . All rights reserved.