.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