我们希望WCF客户端调用采用透明代理方式,不用添加服务引用,也不用Invoke的方式,通过ChannelFactory<>动态产生通道,实现服务接口进行调用,并且支持async/await,当然也不用在Config中配置serviceModel。
服务端代码:
[ServiceContract] public interface IGameService { [OperationContract] Task DoWorkAsync(string arg); [OperationContract] void DoWork(string arg); } public class GameService : IGameService { public async Task<string> DoWorkAsync(string arg) { return await Task.FromResult($"Hello {arg}, I am the GameService."); } public string DoWork(string arg) { return $"Hello {arg}, I am the GameService."; } } [ServiceContract] public interface IPlayerService { [OperationContract] Task<string> DoWorkAsync(string arg); [OperationContract] string DoWork(string arg); } public class PlayerService : IPlayerService { public async Task<string> DoWorkAsync(string arg) { return await Task.FromResult($"Hello {arg}, I am the PlayerService."); } public async string DoWork(string arg) { return $"Hello {arg}, I am the PlayerService."; } }
代理类
动态创建服务对象,ChannelFactory<T>的运用,一个抽象类
namespace Wettery.Infrastructure.Wcf { public enum WcfBindingType { BasicHttpBinding, NetNamedPipeBinding, NetPeerTcpBinding, NetTcpBinding, WebHttpBinding, WSDualHttpBinding, WSFederationHttpBinding, WSHttpBinding } public abstract class WcfChannelClient<TChannel> : IDisposable { public abstract string ServiceUrl { get; } private Binding _binding; public virtual Binding Binding { get { if (_binding == null) _binding = CreateBinding(WcfBindingType.NetTcpBinding); return _binding; } } protected TChannel _channel; public TChannel Channel { get { return _channel; } } protected IClientChannel ClientChannel { get { return (IClientChannel)_channel; } } public WcfChannelClient() { if (string.IsNullOrEmpty(this.ServiceUrl)) throw new NotSupportedException("ServiceUrl is not overridden by derived classes."); var chanFactory = new ChannelFactory<TChannel>(this.Binding, this.ServiceUrl); _channel = chanFactory.CreateChannel(); this.ClientChannel.Open(); } protected virtual void Dispose(bool disposing) { if (disposing && this.ClientChannel != null) { try { this.ClientChannel.Close(TimeSpan.FromSeconds(2)); } catch { this.ClientChannel.Abort(); } } //TODO: free unmanaged resources } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~WcfChannelClient() { Dispose(false); } private static Binding CreateBinding(WcfBindingType binding) { Binding bindinginstance = null; if (binding == WcfBindingType.BasicHttpBinding) { BasicHttpBinding ws = new BasicHttpBinding(); ws.MaxBufferSize = 2147483647; ws.MaxBufferPoolSize = 2147483647; ws.MaxReceivedMessageSize = 2147483647; ws.ReaderQuotas.MaxStringContentLength = 2147483647; ws.CloseTimeout = new TimeSpan(0, 10, 0); ws.OpenTimeout = new TimeSpan(0, 10, 0); ws.ReceiveTimeout = new TimeSpan(0, 10, 0); ws.SendTimeout = new TimeSpan(0, 10, 0); bindinginstance = ws; } else if (binding == WcfBindingType.NetNamedPipeBinding) { NetNamedPipeBinding ws = new NetNamedPipeBinding(); ws.MaxReceivedMessageSize = 65535000; bindinginstance = ws; } else if (binding == WcfBindingType.NetPeerTcpBinding) { //NetPeerTcpBinding ws = new NetPeerTcpBinding(); //ws.MaxReceivedMessageSize = 65535000; //bindinginstance = ws; throw new NotImplementedException(); } else if (binding == WcfBindingType.NetTcpBinding) { NetTcpBinding ws = new NetTcpBinding(); ws.MaxReceivedMessageSize = 65535000; ws.Security.Mode = SecurityMode.None; bindinginstance = ws; } else if (binding == WcfBindingType.WebHttpBinding) { WebHttpBinding ws = new WebHttpBinding(); //Restful style ws.MaxReceivedMessageSize = 65535000; bindinginstance = ws; } else if (binding == WcfBindingType.WSDualHttpBinding) { WSDualHttpBinding ws = new WSDualHttpBinding(); ws.MaxReceivedMessageSize = 65535000; bindinginstance = ws; } else if (binding == WcfBindingType.WSFederationHttpBinding) { WSFederationHttpBinding ws = new WSFederationHttpBinding(); ws.MaxReceivedMessageSize = 65535000; bindinginstance = ws; } else if (binding == WcfBindingType.WSHttpBinding) { WSHttpBinding ws = new WSHttpBinding(SecurityMode.None); ws.MaxReceivedMessageSize = 65535000; ws.Security.Message.ClientCredentialType = MessageCredentialType.Windows; ws.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows; bindinginstance = ws; } return bindinginstance; } } }logs_code_collapse">View Code
针对每个WCF服务派生一个代理类,在其中重写ServiceUrl与Binding,ServiceUrl可以配置到Config中,Binding不重写默认采用NetTcpBinding
public class GameServiceClient : WcfChannelClient<IGameService> { public override string ServiceUrl { get { return "net.tcp://localhost:21336/GameService.svc"; } } } public class PlayerServiceClient : WcfChannelClient<IPlayerService> { public override string ServiceUrl { get { return "net.tcp://localhost:21336/PlayerService.svc"; } } }
客户端调用
using (var client = new GameServiceClient()) { client.Channel.DoWork("thinkpig"); //无返回值 await client.Channel.DoWorkAsync("thinkpig"); //无返回值异步 } using (var client = new PlayerServiceClient()) { var result = client.Channel.DoWork("thinkdog"); //有返回值 result = await client.Channel.DoWorkAsync("thinkdog"); //有返回值异步 }
关于WCF寄宿主机可以参考前两篇文章
WCF绑定netTcpBinding寄宿到控制台应用程序
WCF绑定netTcpBinding寄宿到IIS