zoukankan      html  css  js  c++  java
  • .NET Core调用WCF的最佳实践

    现在.NET Core貌似很火,与其他.NET开发者交流不说上几句.NET Core都感觉自己落伍了一样。但是冷静背后我们要也看到.NET Core目前还有太多不足,别的不多说,与自家的服务框架WCF集成起来就不咋地,从最初不支持,到现在有个笨笨咔咔的Web Service Reference Provider,生成的代理类简直不堪入目,还特别的慢。所以本人本着为将来框架的兼容性做准备,就着手研究了下能不能不通过代理类访问WCF,好在微软开源了一部分WCF代码。

    WCF的开发者一定很熟悉,WCF的所有配置都是可以通过代码和配置文件两种方式,而大多数开发者都会选择配置文件,但是从.NET Core开始,微软干掉了Web/App.config,不知道别人怎么样,反正我是非常之不习惯。干掉Web/App.config的后果,就是开源支持.NET Core的那部分Client Side没有配置文件的支持,翻看源码后发现,只要是读取配置文件的地方,都是这样的代码 —— PlatformNotSupported。

    protected void InitializeEndpoint(string configurationName, EndpointAddress address)
    {
                _serviceEndpoint = this.CreateDescription();
    
                ServiceEndpoint serviceEndpointFromConfig = null;
    
                // Project N and K do not support System.Configuration, but this method is part of Windows Store contract.
                // The configurationName==null path occurs in normal use.
                if (configurationName != null)
                {
                    throw ExceptionHelper.PlatformNotSupported();
                    // serviceEndpointFromConfig = ConfigLoader.LookupEndpoint(configurationName, address, this.serviceEndpoint.Contract);
           }
    }

    但是好在微软又推出了System.Configuration.ConfigurationManager的NuGet包,所以本人仿照WCF原生的配置文件,自己实现一套配置,经过两个晚上的战斗,已经成功。好了,废话不多说,上代码了。

    先看看最终的效果是什么样的,WCF服务端的代码结构如下:

    采用标准的WCF分层方式,用控制台做宿主,其中IAppService项目版本为.NET Standard 2.0,每个终结点同时使用BasicHttpBinding与NetTcpBinding双重绑定,配置文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <!--WCF配置-->
      <system.serviceModel>
        <!--WCF服务配置,手动增加service节点-->
        <services>
          <!--产品服务配置-->
          <service behaviorConfiguration="DefaultBehavior" name="WCF.AppService.Implements.ProductService">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8098/Hosts/ProductService.svc" />
                <add baseAddress="net.tcp://localhost:8099/Hosts/ProductService.svc" />
              </baseAddresses>
            </host>
            <endpoint binding="basicHttpBinding" bindingConfiguration="basicBinding" contract="WCF.IAppService.Interfaces.IProductService" />
            <endpoint binding="netTcpBinding" bindingConfiguration="tcpBinding" contract="WCF.IAppService.Interfaces.IProductService" />
          </service>
          <!--订单服务配置-->
          <service behaviorConfiguration="DefaultBehavior" name="WCF.AppService.Implements.OrderService">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8098/Hosts/OrderService.svc" />
                <add baseAddress="net.tcp://localhost:8099/Hosts/OrderService.svc" />
              </baseAddresses>
            </host>
            <endpoint binding="basicHttpBinding" bindingConfiguration="basicBinding" contract="WCF.IAppService.Interfaces.IOrderService" />
            <endpoint binding="netTcpBinding" bindingConfiguration="tcpBinding" contract="WCF.IAppService.Interfaces.IOrderService" />
          </service>
          <!--集成服务配置-->
          <service behaviorConfiguration="DefaultBehavior" name="WCF.AppService.Implements.IntegrationService">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8098/Hosts/IntegrationService.svc" />
                <add baseAddress="net.tcp://localhost:8099/Hosts/IntegrationService.svc" />
              </baseAddresses>
            </host>
            <endpoint binding="basicHttpBinding" bindingConfiguration="basicBinding" contract="WCF.IAppService.Interfaces.IIntegrationService" />
            <endpoint binding="netTcpBinding" bindingConfiguration="tcpBinding" contract="WCF.IAppService.Interfaces.IIntegrationService" />
          </service>
        </services>
        <!--WCF行为配置,配置好无需修改-->
        <behaviors>
          <serviceBehaviors>
            <behavior name="DefaultBehavior">
              <!--是否允许get请求访问-->
              <serviceMetadata httpGetEnabled="true" />
              <!--允许从请求消息头中检索元数据地址信息-->
              <useRequestHeadersForMetadataAddress />
              <!--是否显示异常信息-->
              <serviceDebug includeExceptionDetailInFaults="true" />
              <!--最大序列化的对象个数-->
              <dataContractSerializer maxItemsInObjectGraph="2147483647" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <!--WCF绑定配置,配置好无需修改-->
        <bindings>
          <netTcpBinding>
            <binding name="tcpBinding" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" />
          </netTcpBinding>
          <basicHttpBinding>
            <binding name="basicBinding" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" />
          </basicHttpBinding>
        </bindings>
        <!--WCF多宿主绑定配置-->
        <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
      </system.serviceModel>
    </configuration>

    运行测试没问题,服务端不多说,重点是客户端,因为IAppService是.NET Standard 2.0的类库版本,所以.NET Core客户端是可以正常引用的,新建.NET Core控制台,并引用上述IAppService项目。

    客户端项目结构如下:

    客户端配置文件如下:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
        <!--WCF配置节点-->
        <section name="system.serviceModel" type="System.ServiceModel.ServiceModelSection, System.ServiceModel.Toolkits" />
      </configSections>
    
      <!--WCF配置-->
      <system.serviceModel>
        <!--WCF客户端配置,手动增加endpoint节点-->
        <client>
          <!--商品服务契约配置-->
          <endpoint address="net.tcp://localhost:8099/Hosts/ProductService.svc" binding="netTcpBinding" contract="WCF.IAppService.Interfaces.IProductService" name="WCF.IAppService.Interfaces.IProductService">
            <headerProvider type="WCF.Core.Client.HeaderProviders.MyHeaderProvider" assembly="WCF.Core.Client"/>
          </endpoint>
          <!--订单服务契约配置-->
          <endpoint address="net.tcp://localhost:8099/Hosts/OrderService.svc" binding="netTcpBinding" contract="WCF.IAppService.Interfaces.IOrderService" name="WCF.IAppService.Interfaces.IOrderService" />
          <!--集成服务契约配置-->
          <endpoint address="net.tcp://localhost:8099/Hosts/IntegrationService.svc" binding="netTcpBinding" contract="WCF.IAppService.Interfaces.IIntegrationService" name="WCF.IAppService.Interfaces.IIntegrationService" />
        </client>
      </system.serviceModel>
    </configuration>

    Main方法中代码如下:

    class Program
        {
            static void Main(string[] args)
            {
                //初始化容器
                IContainer container = InitContainer();
    
                //调用
                IProductService productService = container.Resolve<IProductService>();
                string products = productService.GetProducts();
    
                Console.WriteLine(products);
    
                container.Dispose();
                Console.ReadKey();
            }
    
            static IContainer InitContainer()
            {
                ContainerBuilder builder = new ContainerBuilder();
                Assembly wcfInterfaceAssembly = Assembly.Load("WCF.IAppService");
    
                //获取WCF接口类型集
                IEnumerable<Type> types = wcfInterfaceAssembly.GetTypes().Where(type => type.IsInterface);
    
                //获取服务代理泛型类型
                Type proxyGenericType = typeof(ServiceProxy<>);
    
                //注册WCF接口
                foreach (Type type in types)
                {
                    Type proxyType = proxyGenericType.MakeGenericType(type);
                    PropertyInfo propChannel = proxyType.GetProperty(ServiceProxy.ChannelPropertyName, type);
    
                    builder.RegisterType(proxyType).OnRelease(proxy => ((IDisposable)proxy).Dispose());
                    builder.Register(container => propChannel.GetValue(container.Resolve(proxyType))).
                        As(type).
                        OnRelease(channel => channel.CloseChannel());
                }
    
                return builder.Build();
            }
        }

    启动运行结果如下:

    怎么样?是不是觉得很清爽?如果你有兴趣,可以到我的Git看全部源码,地址如下:

    https://gitee.com/lishilei0523/WCF-NET-Core

    Ps:因为微软公开的WCF类库本身就不完善,所以我也没法提供全部的功能,本人所作调用方式目前支持BasicHttpBinding和NetTcpBinding,并且包含消息头支持。如果你觉得代码对你有帮助,麻烦点个Star,不胜感激。

    欢迎进入我的码云

    https://gitee.com/lishilei0523
  • 相关阅读:
    vim删除某一列
    linux下在当前文件夹查找一个字符串信息
    .tar和.tar.gz的区别
    visual studio中调用masm汇编
    iconv转码失败的原因
    终端查看
    kubectl 常用命令总结
    Charles实战之Charles抓取https请求
    Fiddler实战之拟2G、3G、4G网络进行弱网测试
    Fiddler实战之使用Fiddler模拟弱网环境
  • 原文地址:https://www.cnblogs.com/lishilei0523/p/8886483.html
Copyright © 2011-2022 走看看