VS2022添加服务引用:.NET Framework项目未生成ClientCredentials
.NET Framework服务引用代理缺失ClientCredentials的解决方案
问题场景
为.NET Framework和.NET 8项目添加同一WCF服务引用,.NET 8生成的代理能正常获取ClientCredentials以设置用户名密码,但.NET Framework生成的代理类未显式暴露该属性,导致无法配置身份验证信息。
相关代码片段
WSDL安全策略
<wsp:Policy wsu:Id="WSHttpBinding_IQuery_2012_04_policy"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:TransportToken> <wsp:Policy> <sp:HttpsToken RequireClientCertificate="false" /> </wsp:Policy> </sp:TransportToken> <sp:AlgorithmSuite> <wsp:Policy> <sp:Basic256 /> </wsp:Policy> </sp:AlgorithmSuite> <sp:Layout> <wsp:Policy> <sp:Strict /> </wsp:Policy> </sp:Layout> <sp:IncludeTimestamp /> </wsp:Policy> </sp:TransportBinding> <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken10 /> </wsp:Policy> </sp:UsernameToken> </wsp:Policy> </sp:SignedSupportingTokens> <sp:Wss11 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy /> </sp:Wss11> <sp:Trust10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:MustSupportIssuedTokens /> <sp:RequireClientEntropy /> <sp:RequireServerEntropy /> </wsp:Policy> </sp:Trust10> <wsaw:UsingAddressing /> </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
生成的绑定配置
<system.serviceModel> <bindings> <wsHttpBinding> <binding name="WSHttpBinding_IQuery_2012_04"> <security mode="TransportWithMessageCredential"> <transport clientCredentialType="None" /> <message clientCredentialType="UserName" establishSecurityContext="false" /> </security> </binding> </wsHttpBinding> </bindings> <client> <endpoint address="https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IQuery_2012_04" contract="PTSQueryService.IQuery_2012_04" name="WSHttpBinding_IQuery_2012_04" /> </client> </system.serviceModel>
.NET Framework生成的代理片段
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public partial class Query_2012_04Client : System.ServiceModel.ClientBase<WCFTest.PTSQueryService.IQuery_2012_04>, WCFTest.PTSQueryService.IQuery_2012_04 { public Query_2012_04Client() { } public Query_2012_04Client(string endpointConfigurationName) : base(endpointConfigurationName) { } public Query_2012_04Client(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public Query_2012_04Client(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public Query_2012_04Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public WCFTest.PTSQueryService.QueryParameter[] GetQueryParameters(int qtype, string qname) { return base.Channel.GetQueryParameters(qtype, qname); } public System.Threading.Tasks.Task<WCFTest.PTSQueryService.QueryParameter[]> GetQueryParametersAsync(int qtype, string qname) { return base.Channel.GetQueryParametersAsync(qtype, qname); } public string GetQueryData(int qtype, string qname, WCFTest.PTSQueryService.QueryArgument[] args, int page, int pageSize) { return base.Channel.GetQueryData(qtype, qname, args, page, pageSize); } public System.Threading.Tasks.Task<string> GetQueryDataAsync(int qtype, string qname, WCFTest.PTSQueryService.QueryArgument[] args, int page, int pageSize) { return base.Channel.GetQueryDataAsync(qtype, qname, args, page, pageSize); } }
.NET 8生成的代理片段
[System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.2.0-preview1.23462.5")] public partial class Query_2012_04Client : System.ServiceModel.ClientBase<PTSQueryService.IQuery_2012_04>, PTSQueryService.IQuery_2012_04 { /// <summary> /// Implement this partial method to configure the service endpoint. /// </summary> /// <param name="serviceEndpoint">The endpoint to configure</param> /// <param name="clientCredentials">The client credentials</param> static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials); public Query_2012_04Client() : base(Query_2012_04Client.GetDefaultBinding(), Query_2012_04Client.GetDefaultEndpointAddress()) { this.Endpoint.Name = EndpointConfiguration.WSHttpBinding_IQuery_2012_04.ToString(); ConfigureEndpoint(this.Endpoint, this.ClientCredentials); } public Query_2012_04Client(EndpointConfiguration endpointConfiguration) : base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), Query_2012_04Client.GetEndpointAddress(endpointConfiguration)) { this.Endpoint.Name = endpointConfiguration.ToString(); ConfigureEndpoint(this.Endpoint, this.ClientCredentials); } public Query_2012_04Client(EndpointConfiguration endpointConfiguration, string remoteAddress) : base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), new System.ServiceModel.EndpointAddress(remoteAddress)) { this.Endpoint.Name = endpointConfiguration.ToString(); ConfigureEndpoint(this.Endpoint, this.ClientCredentials); } public Query_2012_04Client(EndpointConfiguration endpointConfiguration, System.ServiceModel.EndpointAddress remoteAddress) : base(Query_2012_04Client.GetBindingForEndpoint(endpointConfiguration), remoteAddress) { this.Endpoint.Name = endpointConfiguration.ToString(); ConfigureEndpoint(this.Endpoint, this.ClientCredentials); } public Query_2012_04Client(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public System.Threading.Tasks.Task<PTSQueryService.QueryParameter[]> GetQueryParametersAsync(int qtype, string qname) { return base.Channel.GetQueryParametersAsync(qtype, qname); } public System.Threading.Tasks.Task<string> GetQueryDataAsync(int qtype, string qname, PTSQueryService.QueryArgument[] args, int page, int pageSize) { return base.Channel.GetQueryDataAsync(qtype, qname, args, page, pageSize); } public virtual System.Threading.Tasks.Task OpenAsync() { return System.Threading.Tasks.Task.Factory.FromAsync(((System.ServiceModel.ICommunicationObject)(this)).BeginOpen(null, null), new System.Action<System.IAsyncResult>(((System.ServiceModel.ICommunicationObject)(this)).EndOpen)); } private static System.ServiceModel.Channels.Binding GetBindingForEndpoint(EndpointConfiguration endpointConfiguration) { if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IQuery_2012_04)) { System.ServiceModel.WSHttpBinding result = new System.ServiceModel.WSHttpBinding(); result.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max; result.MaxReceivedMessageSize = int.MaxValue; result.AllowCookies = true; result.Security.Mode = System.ServiceModel.SecurityMode.TransportWithMessageCredential; result.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.None; result.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.UserName; result.Security.Message.EstablishSecurityContext = false; return result; } throw new System.InvalidOperationException(string.Format("Could not find endpoint with name '{0}'.", endpointConfiguration)); } private static System.ServiceModel.EndpointAddress GetEndpointAddress(EndpointConfiguration endpointConfiguration) { if ((endpointConfiguration == EndpointConfiguration.WSHttpBinding_IQuery_2012_04)) { return new System.ServiceModel.EndpointAddress("https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.s" + "vc"); } throw new System.InvalidOperationException(string.Format("Could not find endpoint with name '{0}'.", endpointConfiguration)); } private static System.ServiceModel.Channels.Binding GetDefaultBinding() { return Query_2012_04Client.GetBindingForEndpoint(EndpointConfiguration.WSHttpBinding_IQuery_2012_04); } private static System.ServiceModel.EndpointAddress GetDefaultEndpointAddress() { return Query_2012_04Client.GetEndpointAddress(EndpointConfiguration.WSHttpBinding_IQuery_2012_04); } public enum EndpointConfiguration { WSHttpBinding_IQuery_2012_04, } }
解决方案
方法1:直接访问基类属性
.NET Framework生成的代理类继承自ClientBase<T>,基类本身包含ClientCredentials属性,无需额外修改,直接通过实例访问即可:
var client = new Query_2012_04Client(); client.ClientCredentials.UserName.UserName = "你的用户名"; client.ClientCredentials.UserName.Password = "你的密码"; // 调用服务方法 var result = client.GetQueryParameters(1, "test_query");
方法2:扩展代理类(推荐)
利用代理类的partial特性,添加扩展部分封装凭证设置逻辑,提升代码复用性:
public partial class Query_2012_04Client { public void SetCredentials(string username, string password) { ClientCredentials.UserName.UserName = username; ClientCredentials.UserName.Password = password; } }
使用时直接调用:
var client = new Query_2012_04Client(); client.SetCredentials("你的用户名", "你的密码");
方法3:重新生成代理
使用svcutil.exe命令行工具重新生成代理,确保凭证相关配置被正确识别:
svcutil.exe https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.svc /out:QueryClient.cs /config:app.config
方法4:手动构造绑定与端点
如果自动生成的代理存在问题,可手动创建绑定和端点,直接初始化客户端:
var binding = new WSHttpBinding(SecurityMode.TransportWithMessageCredential); binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName; binding.Security.Message.EstablishSecurityContext = false; var endpointAddress = new EndpointAddress("https://somehost/ptsqamt/Maintain/Services/Data/2012/04/Query.svc"); var client = new Query_2012_04Client(binding, endpointAddress); client.ClientCredentials.UserName.UserName = "你的用户名"; client.ClientCredentials.UserName.Password = "你的密码";
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

