问题
I have a client for SOAP service in a console application, I need to move the service client to sharepoint 2010. I do not want to do config files deployment and other sharepoint-related stuff, so I decided to hardcode binding information, the only configurable option is URL. But I encountred some troubles doing this. I have a config:
<system.serviceModel>
<bindings>
<customBinding>
<binding name="SI_PMProjectMaintain_SOUTBinding">
<textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Soap11" writeEncoding="utf-8">
<readerQuotas maxDepth="10000000" maxStringContentLength="10000000"
maxArrayLength="67108864" maxBytesPerRead="65536" maxNameTableCharCount="100000" />
</textMessageEncoding>
<httpTransport authenticationScheme="Basic" bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard" keepAliveEnabled="false"
proxyAuthenticationScheme="Basic" realm="XISOAPApps" useDefaultWebProxy="true" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://url/XISOAPAdapter/MessageServlet?senderParty=&senderService=Param1&receiverParty=&receiverService=&interface=interface&interfaceNamespace=url"
binding="customBinding" bindingConfiguration="SI_PMProjectMaintain_SOUTBinding"
contract="PmProjectMaintain.SI_PMProjectMaintain_SOUT" name="HTTP_Port" />
</client>
Also, I have a code:
var service = new ServiceClient();
service.ClientCredentials.Windows.ClientCredential = new NetworkCredential("user", "password");
service.ClientCredentials.UserName.UserName = "user";
service.ClientCredentials.UserName.Password = "password";
var resp = service.Operation();
Console.WriteLine(resp.Response);
It works as expected. Now I want to get rid of xml config and completly move it to code:
public class CustomHttpTransportBinding : CustomBinding
{
public CustomHttpTransportBinding()
{
}
public override BindingElementCollection CreateBindingElements()
{
var result = new BindingElementCollection();
result.Add(new TextMessageEncodingBindingElement
{
MaxReadPoolSize = 64,
MaxWritePoolSize = 16,
MessageVersion = MessageVersion.Soap11,
WriteEncoding = Encoding.UTF8,
//ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas
//{
// MaxDepth = 10000000,
// MaxStringContentLength = 10000000,
// MaxArrayLength = 67108864,
// MaxBytesPerRead = 65536,
// MaxNameTableCharCount = 100000
//}
});
result.Add(new HttpTransportBindingElement
{
AuthenticationScheme = AuthenticationSchemes.Basic,
BypassProxyOnLocal = false,
HostNameComparisonMode = System.ServiceModel.HostNameComparisonMode.StrongWildcard,
KeepAliveEnabled = false,
ProxyAuthenticationScheme = AuthenticationSchemes.Basic,
Realm = "XISOAPApps",
UseDefaultWebProxy = true
});
return result;
}
}
And I use it like this:
var service = new ServiceClient(new CustomHttpTransportBinding(), new EndpointAddress(url));
service.ClientCredentials.Windows.ClientCredential = new NetworkCredential("user", "password");
service.ClientCredentials.UserName.UserName = "user";
service.ClientCredentials.UserName.Password = "password";
var resp = service.Operation();
Console.WriteLine(resp.Response);
It reaches the server, but throws exception ("Server Error"), so I belive something wrong with my configuration. I can not specify the reader quotas also. I've tried loading configuration from string, but it is not worked for me, same error. Confi It seems that I can not convert custom bindings from XML to code in .net 3.5. Can somebody take a look on my code and confirm? Are there other ways to have a service client with custom binding in code?
回答1:
I have not found a way to move configuration from app.config to code for a custom binding, it works greate for basicHttpBinding, but does not work for customBinding.
I've ended up with loading the configuration dynamicaly from file using custom chanell factory.
public class CustomChannelFactory<T> : ChannelFactory<T>
{
private readonly string _configurationPath;
public CustomChannelFactory(string configurationPath) : base(typeof(T))
{
_configurationPath = configurationPath;
base.InitializeEndpoint((string)null, null);
}
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint serviceEndpoint = base.CreateDescription();
ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap();
executionFileMap.ExeConfigFilename = _configurationPath;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None);
ServiceModelSectionGroup serviceModeGroup = ServiceModelSectionGroup.GetSectionGroup(config);
ChannelEndpointElement selectedEndpoint = null;
foreach (ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
{
if (endpoint.Contract == serviceEndpoint.Contract.ConfigurationName)
{
selectedEndpoint = endpoint;
break;
}
}
if (selectedEndpoint != null)
{
if (serviceEndpoint.Binding == null)
{
serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, serviceModeGroup);
}
if (serviceEndpoint.Address == null)
{
serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers);
}
if (serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))
{
AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, serviceModeGroup);
}
serviceEndpoint.Name = selectedEndpoint.Contract;
}
return serviceEndpoint;
}
private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group)
{
BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
if (bindingElementCollection.ConfiguredBindings.Count > 0)
{
IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
Binding binding = GetBinding(be);
if (be != null)
{
be.ApplyConfiguration(binding);
}
return binding;
}
return null;
}
private void AddBehaviors(string behaviorConfiguration, ServiceEndpoint serviceEndpoint, ServiceModelSectionGroup group)
{
EndpointBehaviorElement behaviorElement = group.Behaviors.EndpointBehaviors[behaviorConfiguration];
for (int i = 0; i < behaviorElement.Count; i++)
{
BehaviorExtensionElement behaviorExtension = behaviorElement[i];
object extension = behaviorExtension.GetType().InvokeMember("CreateBehavior",
BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
null, behaviorExtension, null);
if (extension != null)
{
serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension);
}
}
}
private EndpointIdentity GetIdentity(IdentityElement element)
{
EndpointIdentity identity = null;
PropertyInformationCollection properties = element.ElementInformation.Properties;
if (properties["userPrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value);
}
if (properties["servicePrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value);
}
if (properties["dns"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateDnsIdentity(element.Dns.Value);
}
if (properties["rsa"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value);
}
if (properties["certificate"].ValueOrigin != PropertyValueOrigin.Default)
{
X509Certificate2Collection supportingCertificates = new X509Certificate2Collection();
supportingCertificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue));
if (supportingCertificates.Count == 0)
{
throw new InvalidOperationException("UnableToLoadCertificateIdentity");
}
X509Certificate2 primaryCertificate = supportingCertificates[0];
supportingCertificates.RemoveAt(0);
return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates);
}
return identity;
}
private Binding GetBinding(IBindingConfigurationElement configurationElement)
{
if (configurationElement is CustomBindingElement)
return new CustomBinding();
else if (configurationElement is BasicHttpBindingElement)
return new BasicHttpBinding();
else if (configurationElement is NetMsmqBindingElement)
return new NetMsmqBinding();
else if (configurationElement is NetNamedPipeBindingElement)
return new NetNamedPipeBinding();
else if (configurationElement is NetPeerTcpBindingElement)
return new NetPeerTcpBinding();
else if (configurationElement is NetTcpBindingElement)
return new NetTcpBinding();
else if (configurationElement is WSDualHttpBindingElement)
return new WSDualHttpBinding();
else if (configurationElement is WSHttpBindingElement)
return new WSHttpBinding();
else if (configurationElement is WSFederationHttpBindingElement)
return new WSFederationHttpBinding();
return null;
}
}
Usefull links:
- Loading WCF configuration from different files (source code is not availiable)
- Thread with source code of CustomChannelFactory
- .NET 4.0 ChanellFactory (wish it was in .net 3.5)
回答2:
Found an issue with the code referenced in boades reply, in that if you have mixed transport bindings (https
/http
) under basicHttpbinding
you are likely to get an exception similar/reverse of this:
The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via
I'd also expect you will also have unexpected authorization occurring, again as the code will use the first bindingConfiguration
listed in the web.config
rather than by name.
The offending line which doesn't take the binding by name, but rather just takes the 1st one(!)
IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
This could be corrected by updating CreateBinding
method and the call in CreateDescription
like so:
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint description = base.CreateDescription();
if (CustomisedChannelFactory<TChannel>.ConfigurationPath == null || !System.IO.File.Exists(CustomisedChannelFactory<TChannel>.ConfigurationPath))
return base.CreateDescription();
ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
{
ExeConfigFilename = CustomisedChannelFactory<TChannel>.ConfigurationPath
}, ConfigurationUserLevel.None));
ChannelEndpointElement channelEndpointElement1 = (ChannelEndpointElement)null;
foreach (ChannelEndpointElement channelEndpointElement2 in (ConfigurationElementCollection)sectionGroup.Client.Endpoints)
{
if (channelEndpointElement2.Contract == description.Contract.ConfigurationName)
{
channelEndpointElement1 = channelEndpointElement2;
break;
}
}
if (channelEndpointElement1 != null)
{
if (description.Binding == null)
description.Binding = this.CreateBinding(channelEndpointElement1.Binding, channelEndpointElement1.BindingConfiguration, sectionGroup);
if (description.Address == (EndpointAddress)null)
description.Address = new EndpointAddress(channelEndpointElement1.Address, this.GetIdentity(channelEndpointElement1.Identity), channelEndpointElement1.Headers.Headers);
if (description.Behaviors.Count == 0 && !string.IsNullOrEmpty(channelEndpointElement1.BehaviorConfiguration))
this.AddBehaviors(channelEndpointElement1.BehaviorConfiguration, description, sectionGroup);
description.Name = channelEndpointElement1.Contract;
}
return description;
}
private Binding CreateBinding(string bindingName, string bindingConfigurationName, ServiceModelSectionGroup group)
{
BindingCollectionElement collectionElement = group.Bindings[bindingName];
if (collectionElement.ConfiguredBindings.Count <= 0)
return (Binding)null;
IBindingConfigurationElement configurationElement = null;
foreach (IBindingConfigurationElement bce in collectionElement.ConfiguredBindings)
{
if (bce.Name.Equals(bindingConfigurationName))
{
configurationElement = bce;
break;
}
}
if (configurationElement == null) throw new Exception("BindingConfiguration " + bindingConfigurationName + " not found under binding " + bindingName);
Binding binding = this.GetBinding(configurationElement);
if (configurationElement != null)
configurationElement.ApplyConfiguration(binding);
return binding;
}
来源:https://stackoverflow.com/questions/26102499/convert-web-service-configuration-to-code