zoukankan      html  css  js  c++  java
  • Wcf通讯基础框架方案(三)——客户端

    假设定义了一个服务契约:

    [ServiceContract(Namespace = "WcfExtension.Services.Interface")]
        public interface ITestService
        {
            [OperationContract]
            int Add(int x, int y);
    
            [OperationContract]
            [ServiceKnownType(typeof(TestContract))]
            ITestContract TestData(ITestContract tc);
            
            [OperationContract]
            List<string> AddList(List<string> args);
        }

    首先看一下,客户端是怎么使用的:

    Console.WriteLine(WcfServiceLocator.Create<ITestService>().Add(1, 3));

    WcfServiceLocator.Create()出来的就是一个接口,可以保持这个引用以后调用,也可以每次直接这么写,那么来看看WcfServiceLocator:

        public static T Create<T>() where T : class
            {
                return (T)(new ServiceRealProxy<T>().GetTransparentProxy());
            }
    
            public static IWcfLogService GetLogService()
            {
                if (enableRemoteLogService)
                    return (IWcfLogService)(new LogServiceRealProxy().GetTransparentProxy());
                else
                    return new LocalLogService();
            }

    Create方法很简单,只是返回一个ServiceRealProxy的透明代理。这个之后会详细说,下面单独有一个GetLogService专门用于获取日志服务。这里通过心跳来判断远程的日志服务是否可用,如果不可用直接返回本地的日志服务。之所以日志服务的真实代理和其它业务服务的真实代理不同是因为,在业务服务中我们需要加入很多横切日志,在日志服务中不需要:

        public override IMessage Invoke(IMessage msg)
            {
                using (var client = WcfServiceClientFactory.CreateServiceClient<T>())
                {
                    var channel = client.Channel;
                    IMethodCallMessage methodCall = (IMethodCallMessage)msg;
                    IMethodReturnMessage methodReturn = null;
                    object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
                    methodCall.Args.CopyTo(copiedArgs, 0);
    
                    bool isSuccessuful = false;
                    var stopwatch = Stopwatch.StartNew();
    
                    try
                    {
    #if DEBUG
                        Console.WriteLine("开始调用:" + methodCall.MethodName);
    #endif
                        object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
    
    #if DEBUG
                        if (ClientApplicationContext.Current != null)
                            Console.WriteLine("这个请求由" + ClientApplicationContext.Current.ServerMachineName + "处理完成,服务端版本号:" + ClientApplicationContext.Current.ServerVersion);
                        Console.WriteLine("结束调用:" + methodCall.MethodName);
    
    #endif
                        methodReturn = new ReturnMessage(returnValue,
                                                        copiedArgs,
                                                        copiedArgs.Length,
                                                        methodCall.LogicalCallContext,
                                                        methodCall);
                        isSuccessuful = true;
                    }
                    catch (Exception ex)
                    {
                        var excepionID = ClientApplicationContext.Current.ServerExceptionID ?? "";
                        if (ex.InnerException != null)
                        {
    #if DEBUG
                            Console.WriteLine("调用服务端方法出现异常:" + ex.InnerException.Message);
    #endif
                            var exception = ex.InnerException;
                            if (exception is FaultException)
                            {
                                exception = new Exception("Failed to call this Wcf service, remote exception message is:" + exception.Message);
                                exception.HelpLink = "Please check this exception via ID : " + excepionID;
                            }
    
                            if (WcfLogManager.Current().ExceptionInfo.Client.Enabled)
                            {
    
                                var log = WcfLogProvider.GetClientExceptionInfo(
                                    typeof(T).FullName,
                                    ClientApplicationContext.Current.RequestIdentity,
                                    "ServiceRealProxy.Invoke",
                                    exception, excepionID);
                                WcfServiceLocator.GetLogService().Log(log);
    
                            }
                            methodReturn = new ReturnMessage(exception, methodCall);
                        }
                        else
                        {
                            Console.WriteLine("调用方法出现异常:" + ex.Message);
                            if (WcfLogManager.Current().ExceptionInfo.Client.Enabled)
                            {
                                var log = WcfLogProvider.GetClientExceptionInfo(
                                    typeof(T).FullName,
                                    ClientApplicationContext.Current.RequestIdentity,
                                    "ServiceRealProxy.Invoke",
                                    ex, excepionID);
                                WcfServiceLocator.GetLogService().LogWithoutException(log);
                                methodReturn = new ReturnMessage(ex, methodCall);
                            }
                        }
                    }
                    finally
                    {
                        if (WcfLogManager.Current<T>().InvokeInfo.Client.Enabled)
                        {
                            var log = WcfLogProvider.GetClientInvokeLog(
                               typeof(T).FullName,
                               "ServiceRealProxy.Invoke",
                               stopwatch.ElapsedMilliseconds,
                               isSuccessuful,
                               methodCall.MethodName,
                               ClientApplicationContext.Current);
                            WcfServiceLocator.GetLogService().LogWithoutException(log);
                        }
                    }
                    return methodReturn;
                }
            }

    这就是ServiceRealProxy的Invoke方法实现了,可以看到:

    1) 在这里我们using了WcfServiceClientFactory.CreateServiceClient返回的包装后的Channel,让每一次调用都可以正常关闭信道。

    2) 在这里我们加入了调用方法成功后的日志,以及出错时的异常日志。

    再来看一下WcfChannelWrapper:

    internal sealed class WcfChannelWrapper<T> : IDisposable where T : class
        {
            private readonly T channel;
    
            public WcfChannelWrapper(T channel)
            {
                this.channel = channel;
            }
    
            public T Channel { get { return channel; } }
    
            #region IDisposable Members
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            private bool disposed;
    
            private void Dispose(bool disposing)
            {
                if (disposed) return;
                if (disposing)
                {
                    var commObj = channel as ICommunicationObject;
                    if (commObj != null)
                    {
                        try
                        {
                            commObj.Close();
                        }
                        catch (CommunicationException)
                        {
                            commObj.Abort();
                        }
                        catch (TimeoutException)
                        {
                            commObj.Abort();
                        }
                        catch (Exception)
                        {
                            commObj.Abort();
                            throw;
                        }
                    }
    
                }
    
                disposed = true;
            }
    
            ~WcfChannelWrapper()
            {
                Dispose(false);
            }
    
            #endregion
    
        }

    使用最佳实践推荐的方式来关闭信道。ServiceRealProxy的另一个好处是,不再需要这么调用服务:

                using (var scf = WcfServiceClientFactory.CreateServiceClient<IWcfConfigService>())
                    {
                        return scf.Channel.GetWcfClientEndpoint(serviceContractType.FullName, versionstring, WcfLogProvider.MachineName);
                    }

    使用的直接是契约接口,不需要scf.Channel.xx(),不需要using。最后,来看看LogService的真实代理,干净多了:

            public override IMessage Invoke(IMessage msg)
            {
                using (var client = WcfServiceClientFactory.CreateServiceClient<IWcfLogService>())
                {
                    var channel = client.Channel;
                    IMethodCallMessage methodCall = (IMethodCallMessage)msg;
                    IMethodReturnMessage methodReturn = null;
                    object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[];
                    methodCall.Args.CopyTo(copiedArgs, 0);
                    try
                    {
                        object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs);
                        methodReturn = new ReturnMessage(returnValue,
                                                        copiedArgs,
                                                        copiedArgs.Length,
                                                        methodCall.LogicalCallContext,
                                                        methodCall);
                    }
                    catch (Exception ex)
                    {
                        if (ex.InnerException != null)
                        {
                            LocalLogService.Log(ex.InnerException.ToString());
                            methodReturn = new ReturnMessage(ex.InnerException, methodCall);
                        }
                        else
                        {
                            LocalLogService.Log(ex.ToString());
                            methodReturn = new ReturnMessage(ex, methodCall);
                        }
                    }
                    return methodReturn;
                }
            }

    即使出错也不会再调用日志服务,而是记录本地日志,实现了一个比较简单的本地日志服务:

        static LocalLogService()
            {
                var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log");
                if (!Directory.Exists(logPath))
                    Directory.CreateDirectory(logPath);
                Debug.Listeners.Add(new TextWriterTraceListener(logPath + "/log" + DateTime.Now.ToString("yyyyMMdd") + ".txt"));
                Debug.AutoFlush = true;
            }
    
            public static void Log(string text)
            {
                Debug.WriteLine("================================== START ======================================");
                Debug.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                Debug.WriteLine(text);
                Debug.WriteLine("=================================== END =======================================");
            }

    下一节会详细介绍下四种日志消息:启动日志、调用日志、收发消息日志和异常日志。

  • 相关阅读:
    IntelliJ Idea 授权服务器使用
    git 查看对比分支commit命令笔记
    手动搭建一个webpack+react笔记
    并查集的初步认识
    多视口的实现
    对相机的理解及使用多相机绘制只旋转的坐标系
    拾取模型的原理及其在THREE.JS中的代码实现
    面向工程领域的图形平台介绍
    ftgl 绘制文字
    occ 中绘制二维矩形
  • 原文地址:https://www.cnblogs.com/lovecindywang/p/2031624.html
Copyright © 2011-2022 走看看