如何自动生成ChannelFactory所需的WCF OperationContract(Action, ReplyAction)
动态生成WCF客户端契约实现无硬编码自动连接
方案1:基于WSDL元数据自动生成客户端代理
利用.NET的元数据导入工具,直接从目标服务的WSDL地址提取契约信息,自动生成包含正确Action和ReplyAction的客户端类型,完全消除硬编码。
实现代码
using System.ServiceModel; using System.ServiceModel.Description; using System.Reflection; public async Task<(string HelloWorldResult, string VersionResult, bool IsSuccess)> MonitorWcfService(string wsdlAddress, string serviceName) { // 初始化元数据客户端,指定WSDL地址 var mexClient = new MetadataExchangeClient(new Uri(wsdlAddress), MetadataExchangeClientMode.HttpGet); mexClient.ResolveMetadataReferences = true; // 获取服务元数据集合 var metadataSet = await mexClient.GetMetadataAsync(); // 导入元数据生成契约定义 var importer = new WsdlImporter(metadataSet); var contractImports = importer.ImportAllContracts(); var generator = new ServiceContractGenerator(); // 生成契约类型 foreach (var contract in contractImports) { generator.GenerateServiceContractType(contract); } // 动态编译生成的类型到内存程序集 var assemblyName = new AssemblyName($"DynamicWcfClient_{Guid.NewGuid()}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule("DynamicWcfClientModule"); generator.GenerateCode(moduleBuilder); // 获取目标服务的客户端类型和契约接口 var clientType = assemblyBuilder.GetTypes().First(t => t.BaseType == typeof(ClientBase<>)); var contractInterface = clientType.GetInterfaces().First(i => i.GetCustomAttribute<ServiceContractAttribute>() != null); // 配置绑定(匹配你的安全需求) var binding = new BasicHttpBinding(); binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm; binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Ntlm; // 构建端点地址(移除WSDL后缀,使用实际服务地址) var serviceAddress = wsdlAddress.Replace("?wsdl", ""); var endpoint = new EndpointAddress(serviceAddress); // 实例化客户端并调用方法 var clientInstance = Activator.CreateInstance(clientType, binding, endpoint); var helloWorldMethod = contractInterface.GetMethod("HelloWorld"); var helloWorldResult = await (Task<string>)helloWorldMethod.Invoke(clientInstance, null); var getVersionMethod = contractInterface.GetMethod("GetVersion"); var versionResult = await (Task<string>)getVersionMethod.Invoke(clientInstance, null); // 关闭客户端资源 var closeMethod = clientType.GetMethod("Close"); closeMethod.Invoke(clientInstance, null); return (helloWorldResult, versionResult, true); }
关键说明
- 需确保目标WCF服务已启用元数据发布(通过
ServiceMetadataBehavior配置) - 动态生成的程序集仅在内存中运行,无需磁盘存储
- 自动适配服务的契约细节,无需手动拼接
Action路径
方案2:手动构建动态契约(轻量场景)
如果不需要完全自动化的代理生成,可手动构建契约描述,动态指定Action和ReplyAction,适合固定调用HelloWorld和GetVersion的监控场景。
实现代码
using System.ServiceModel; using System.ServiceModel.Description; using System.Xml; public async Task<(string HelloWorldResult, string VersionResult)> ConnectWithDynamicContract(string serviceAddress, string serviceName) { // 构建服务契约描述 var contractDescription = new ContractDescription("IWcfService"); contractDescription.Namespace = "http://tempuri.org/"; // 添加HelloWorld操作,动态生成Action和ReplyAction var helloWorldOp = new OperationDescription("HelloWorld", contractDescription); helloWorldOp.Messages.Add(MessageDescription.CreateInputMessageDescription( new XmlQualifiedName("HelloWorld", $"http://tempuri.org/{serviceName}"))); helloWorldOp.Messages.Add(MessageDescription.CreateOutputMessageDescription( new XmlQualifiedName("HelloWorldResponse", $"http://tempuri.org/{serviceName}"))); helloWorldOp.Messages[0].Action = $"http://tempuri.org/{serviceName}/HelloWorld"; helloWorldOp.Messages[1].Action = $"http://tempuri.org/{serviceName}/HelloWorldResponse"; contractDescription.Operations.Add(helloWorldOp); // 添加GetVersion操作 var getVersionOp = new OperationDescription("GetVersion", contractDescription); getVersionOp.Messages.Add(MessageDescription.CreateInputMessageDescription( new XmlQualifiedName("GetVersion", $"http://tempuri.org/{serviceName}"))); getVersionOp.Messages.Add(MessageDescription.CreateOutputMessageDescription( new XmlQualifiedName("GetVersionResponse", $"http://tempuri.org/{serviceName}"))); getVersionOp.Messages[0].Action = $"http://tempuri.org/{serviceName}/GetVersion"; getVersionOp.Messages[1].Action = $"http://tempuri.org/{serviceName}/GetVersionResponse"; contractDescription.Operations.Add(getVersionOp); // 配置绑定和端点 var binding = new BasicHttpBinding(); binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm; binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Ntlm; var endpoint = new EndpointAddress(serviceAddress); // 创建通道工厂并生成通道 var channelFactory = new ChannelFactory(contractDescription, binding, endpoint); var channel = channelFactory.CreateChannel(); // 调用目标方法 var helloWorldResult = await (Task<string>)channel.GetType().GetMethod("HelloWorld").Invoke(channel, null); var versionResult = await (Task<string>)channel.GetType().GetMethod("GetVersion").Invoke(channel, null); // 释放资源 ((IClientChannel)channel).Close(); channelFactory.Close(); return (helloWorldResult, versionResult); }
关键说明
- 需确保目标服务的命名空间为
http://tempuri.org/,若不同需从WSDL解析或传入参数 - 仅需预先知道要调用的方法名,适合固定监控逻辑的场景
方案对比
- 方案1:完全自动化,适配任意符合WSDL规范的WCF服务,扩展性强
- 方案2:轻量高效,无需动态编译,适合固定调用特定方法的监控场景
内容的提问来源于stack exchange,提问作者iVelesco
相关产品推荐
相关产品推荐

