我有一个使用 Prism 的 WPF 应用程序。
该应用程序加载了几个模块。
每个模块都连接到一个或多个 WCF 服务。
连接详细信息位于模块库的“app.config”文件中。
我的问题是 - 我如何让“Shell”项目了解分布在不同程序集中的不同“app.config”文件中的端点配置。
尝试连接的不同“模块”中的每个客户端 - 抛出无法找到“端点”信息的异常......
我有一个使用 Prism 的 WPF 应用程序。
该应用程序加载了几个模块。
每个模块都连接到一个或多个 WCF 服务。
连接详细信息位于模块库的“app.config”文件中。
我的问题是 - 我如何让“Shell”项目了解分布在不同程序集中的不同“app.config”文件中的端点配置。
尝试连接的不同“模块”中的每个客户端 - 抛出无法找到“端点”信息的异常......
更新:
另一种可能的解决方案是在自己的 AppDomain 中创建 WCF 客户端。
请参阅此处了解一些想法:
我想问题是如何让这个与 Prism 一起工作......诀窍可能是有一个自定义 IModuleManager (Prism V4)、IModuleLoader (V1) 或 Catalog 来处理加载 WCF 客户端模块,或者可能有一个包装器模块这反过来会加载您的 WCF 客户端。
https://prism.svn.codeplex.com/svn/V1/spikes/AGCompositeApplicationLibrary/AGComposite/Modularity/ModuleLoader.cs (来自Prism V1……仅供参考)。
我第一次尝试做类似于你正在做的事情是通过在我的 DLL 模块中执行此操作来破解 AppDomain 配置。
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");
}
我记不清了,但我认为我根本不必在我的主应用程序 app.config 中定义任何 ServiceModel 配置(否则在尝试替换它时会发生冲突)......这可能是错误的...... . 可能正好相反,或者根本没有任何 app.config !:o/。由于某种原因它很脆……我不记得了。
我还尝试在运行时从 ConfigurationManager 访问 ServiceModel 配置,并尝试通过代码对其进行修改……但它没有。
无论如何,我认为以上内容不会对您有所帮助,因为您将加载多个模块,因此需要加载多个配置。
所以无论如何,在尝试了上述方法之后,我通过使用以下组合切换到了一种不那么脆弱的方法:
ExceptionHandlingProxyBase<T>
从
和
CustomClientChannelFactory<T>
从
我进行了一些必要的修复以使 ChannelFactory 工作,并对其进行了修改,以便我可以选择是否要使用我自己的配置文件覆盖,并且还支持覆盖地址。
我在 ExceptionHandlingProxyBase 中使用了这个构造函数来创建工厂:
public CustomClientChannel(Binding binding, string remoteAddress, string configurationPath)
您可以忽略此解决方案的 ExceptionHandlingProxyBase 部分......这只是在通道出现故障时重新建立通道的糖,这样您就不必担心代理的状态。
如果您仍然不想使用 ChannelFactory,那么您可以尝试破解 AppDomain 中的 ServiceModel 配置。我试过了,但似乎很难修改
这是带有修复程序的 ChannelFactory 代码(愚蠢地重命名为 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);
}
}
}