Override WCF client settings for custom config file using DuplexChannel
WCF config files are a great thing, once you have everything configured you just need 2 or 3 lines of code and everything get parsed and you are able to consume the service. By default WCF configuration is stored in the app.config or web.config of the main application, but sometimes (as was my case) the client is in-process of another unknown application, if so the WCF config file will have to be named SomeApp.exe.config and would need to be placed in the same folder as the application. Dealing with this can make things difficult, this is when you have to create your own ChannelFactory
public CustomChannelFactory(string configurationPath)
: base(typeof(T))
{
this.configurationPath = configurationPath;
}
and the override CreateDescription method in which we set our config file. This works great, but not for my case since I was using a DuplexChannel, so when we inherith from DuplexChannelFactory
public class CustomDuplexChannelFactory<TChannel> : DuplexChannelFactory<TChannel>
{
/// <summary>
/// Gets or sets the configuration path.
/// </summary>
/// <value>The configuration path.</value>
public static string ConfigurationPath { get; set; }
/// <summary>
/// Custom client channel constructor.
/// </summary>
public CustomDuplexChannelFactory(InstanceContext callbackInstance)
: base(callbackInstance)
{
}
/// <summary>
/// Overrides the CreateDescription() method of the channel factory
/// to apply a new configuration file.
/// </summary>
/// <returns></returns>
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint serviceEndpoint = base.CreateDescription();
if(ConfigurationPath == null || !File.Exists(ConfigurationPath))
return 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;
}
#region private
/// <summary>
/// Configures the binding for the selected endpoint.
/// </summary>
/// <param name="bindingName"></param>
/// <param name="group"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Adds the configured behavior to the selected endpoint
/// </summary>
/// <param name="behaviorConfiguration"></param>
/// <param name="serviceEndpoint"></param>
/// <param name="group"></param>
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);
}
}
}
/// <summary>
/// Gets the endpoint identity from the configuration file
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Helper method to create the right binding depending on the configuration element
/// </summary>
/// <param name="configurationElement"></param>
/// <returns></returns>
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;
}
}