UPDATE:
An alternative potential solution is to create the WCF clients in their own AppDomains.
See here for some ideas:
I guess the question is how to get this working with Prism...the trick might be to have a custom IModuleManager (Prism V4), IModuleLoader (V1) or Catalog to deal with loading your WCF client modules, or perhaps have a wrapper module that in turn loads your WCF clients.
My first attempt at doing something similar to what you are doing was to hack the AppDomain configuration by doing this in my DLL module.
object o = AppDomain.CurrentDomain.GetData("APP_CONFIG_FILE");
// See if there is a configuration defined by the Exe host, hosting this DLL.
Configuration con = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Do we need to override the configuration, to point to our "default" one?
if (con.HasFile == false)
{
string sFullPathToConfig = Assembly.GetExecutingAssembly().Location + ".config";
AppDomain.CurrentDomain.SetData("APP_CONFIG_FILE", sFullPathToConfig);
ConfigurationManager.RefreshSection("system.serviceModel");
ConfigurationManager.RefreshSection("system.diagnostics");
}
I can't remember exactly, but I think I had to NOT define any ServiceModel configuration at all in my main application app.config (otherwise there would be a conflict in trying to replace it)...could be wrong on that...might have been the opposite, or might have been not have any app.config at all ! :o/. It was brittle for some reason....which I can't remember off the top of my head.
I also tried getting access to the ServiceModel configuration from the ConfigurationManager at runtime and trying to modify that by code...but it would have none of it.
Anyhow, I don't think the above will help you as you will be loading multiple modules, so need to load multiple configs.
So anyhow after trying the above I switched to a less brittle method by using a combination of:
ExceptionHandlingProxyBase<T>
from
and
CustomClientChannelFactory<T>
from
I put in some necessary fixes to get the ChannelFactory to work, and I modified it so I could choose if I wanted to override with my own configuration file, and also to support override of the address.
I used this constructor within the ExceptionHandlingProxyBase to create the factory:
public CustomClientChannel(Binding binding, string remoteAddress, string configurationPath)
You can disregard the ExceptionHandlingProxyBase part of this solution...that is just sugar that re-establishes the channel whenever the channel faults, so that you don't have to worry about the state of your proxy.
If you still don't want to use a ChannelFactory, then you could try hacking the ServiceModel configuration in your AppDomain. I tried that, but it seemed to be hard to modify
Here is the ChannelFactory code with the fixes in (stupidly renamed to CustomClientChannel).
/// <summary>
/// Custom client channel. Allows to specify a different configuration file
/// </summary>
/// <typeparam name="T"></typeparam>
public class CustomClientChannel<T> : ChannelFactory<T>
{
string configurationPath;
string endpointConfigurationName;
bool m_bOverrideConfiguration = false;
Uri m_OverrideAddress = null;
/// <summary>
/// Constructor
/// </summary>
/// <param name="configurationPath"></param>
public CustomClientChannel(string configurationPath) : base(typeof(T))
{
this.configurationPath = configurationPath;
base.InitializeEndpoint((string)null, null);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="binding"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(Binding binding, string configurationPath)
: this(binding, (EndpointAddress)null, configurationPath)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="serviceEndpoint"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(ServiceEndpoint serviceEndpoint, string configurationPath)
: base(typeof(T))
{
this.configurationPath = configurationPath;
base.InitializeEndpoint(serviceEndpoint);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="endpointConfigurationName"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(string endpointConfigurationName, string configurationPath)
: this(endpointConfigurationName, null, configurationPath)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="binding"></param>
/// <param name="endpointAddress"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(Binding binding, EndpointAddress endpointAddress, string configurationPath)
: base(typeof(T))
{
this.configurationPath = configurationPath;
base.InitializeEndpoint(binding, endpointAddress);
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="binding"></param>
/// <param name="remoteAddress"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(Binding binding, string remoteAddress, string configurationPath)
: this(binding, new EndpointAddress(remoteAddress), configurationPath)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="endpointConfigurationName"></param>
/// <param name="endpointAddress"></param>
/// <param name="configurationPath"></param>
public CustomClientChannel(string endpointConfigurationName, EndpointAddress endpointAddress, string configurationPath)
: base(typeof(T))
{
m_OverrideAddress = (endpointAddress != null ? endpointAddress.Uri : null);
this.configurationPath = configurationPath;
this.endpointConfigurationName = endpointConfigurationName;
base.InitializeEndpoint(endpointConfigurationName, endpointAddress);
}
/// <summary>
/// Loads the serviceEndpoint description from the specified configuration file
/// </summary>
/// <returns></returns>
protected override ServiceEndpoint CreateDescription()
{
if (string.IsNullOrEmpty(this.configurationPath))
{
System.Diagnostics.Debug.WriteLine("Not using overriding config file");
return base.CreateDescription();
}
if (!System.IO.File.Exists(configurationPath))
{
System.Diagnostics.Debug.WriteLine("Overriding config file [" + configurationPath + "] doesn't exist");
return base.CreateDescription();
}
m_bOverrideConfiguration = true;
ServiceEndpoint serviceEndpoint = base.CreateDescription();
if (endpointConfigurationName != null)
serviceEndpoint.Name = endpointConfigurationName;
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = this.configurationPath;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
ServiceModelSectionGroup group = ServiceModelSectionGroup.GetSectionGroup(config);
ChannelEndpointElement selectedEndpoint = null;
foreach (ChannelEndpointElement endpoint in group.Client.Endpoints)
{
if (endpoint.Contract == serviceEndpoint.Contract.ConfigurationName &&
(this.endpointConfigurationName == null || this.endpointConfigurationName == endpoint.Name))
{
selectedEndpoint = endpoint;
break;
}
}
if (selectedEndpoint != null)
{
if (serviceEndpoint.Binding == null)
{
serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, selectedEndpoint.BindingConfiguration, group);
}
if (m_OverrideAddress != null)
{
serviceEndpoint.Address = new EndpointAddress(m_OverrideAddress, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers);
}
else
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, group);
}
serviceEndpoint.Name = selectedEndpoint.Contract;
}
return serviceEndpoint;
}
/// <summary>
/// Configures the binding for the selected endpoint
/// </summary>
/// <param name="bindingName"></param>
/// <param name="group"></param>
/// <returns></returns>
private Binding CreateBinding(string bindingName, string bindingConfiguration, ServiceModelSectionGroup group)
{
IBindingConfigurationElement be = null;
BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
if (bindingElementCollection.ConfiguredBindings.Count > 0)
{
foreach (IBindingConfigurationElement bindingElem in bindingElementCollection.ConfiguredBindings)
{
if (string.Compare(bindingElem.Name, bindingConfiguration) == 0)
{
be = bindingElem;
break;
}
}
Binding binding = null;
if (be != null)
{
binding = GetBinding(be);
be.ApplyConfiguration(binding);
}
return binding;
}
return null;
}
/// <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;
}
/// <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)
{
if (group.Behaviors.EndpointBehaviors.Count == 0)
return;
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;
}
protected override void ApplyConfiguration(string configurationName)
{
if (!m_bOverrideConfiguration)
{
// This picks up the configuration from the inherited config settings defined
// by the application i.e. the normal place.
base.ApplyConfiguration(configurationName);
}
}
}