.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
原文地址:https://www.cnblogs.com/lishilei0523/p/8886483.html