Converting web service configuration to code
I have a client for a SOAP service in a console application, I need to move the service client to sharepoint 2010. I don't want to deploy config files and other sharepoint related stuff, so I decided to hard-link the required information, the only configurable option is the URL. But I ran into some trouble. 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 some 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);
Works as expected. Now I want to get rid of the xml config and move it completely into 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 an exception ("Server Error"), so I believe in the wrong configuration. I also cannot specify reader quotas. I tried loading config from string but it doesn't work for me, same error. Confi I can't seem to convert the custom bindings from XML to code in .net 3.5. Can someone take a look at my code and confirm? Are there other ways to have a custom binding service client in code?
source to share
I haven't found a way to move the config from app.config to code for custom binding, it greate works for basicHttpBinding but doesn't work for customBinding.
I ended up loading the dynamic config from a file using a 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;
}
}
Useful links:
source to share
Encountered an issue with the code referenced in boades' answer, in case you have mixed transport bindings ( https
/ http
) in the section basicHttpbinding
you will probably get an exception like / inverse:
The above URI "https" scheme is not valid; expected "http". Parameter name: via
I also expect you to have unexpected authorization as well, since the code will use the first bindingConfiguration
one listed in web.config
, not by name.
An offensive string that doesn't accept anchor by name, but just takes the 1st (!)
IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings [0];
This can be fixed by updating the method CreateBinding
and calling in the CreateDescription
following way:
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;
}
source to share