简体   繁体   English

具有多个WCF客户端的WPF应用程序

[英]WPF application with multiple WCF clients

I have a WPF application using Prism. 我有一个使用Prism的WPF应用程序。

The application loads several modules. 该应用程序加载几个模块。

Each module connects to one or more WCF Service. 每个模块都连接到一个或多个WCF服务。

The connection details are in the 'app.config' file of the Module library. 连接详细信息位于模块库的“ app.config”文件中。

My question is - how do I make the 'Shell' project know about the endpoint configurations that are spread across different 'app.config' files in different assemblies. 我的问题是-如何使'Shell'项目了解分散在不同程序集中不同'app.config'文件中的端点配置。

Each client in different 'Module' that tries to connect - throws an exception that the 'endpoint' information could not be found... 尝试连接的不同“模块”中的每个客户端都会抛出一个异常,即找不到“端点”信息...

UPDATE: 更新:

An alternative potential solution is to create the WCF clients in their own AppDomains. 另一种可能的解决方案是在自己的AppDomain中创建WCF客户端。

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. 我想问题是如何与Prism一起使用...窍门可能是拥有自定义的IModuleManager(Prism V4),IModuleLoader(V1)或Catalog来处理加载WCF客户端模块,或者可能具有包装器模块依次加载您的WCF客户端。


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. 我做与您正在做的事情类似的第一次尝试是通过在我的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");
    }

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 ! 我记不清了,但是我想我根本不必在主应用程序app.config中定义任何ServiceModel配置(否则在尝试替换它时会有冲突)...可能是错误的。 .might相反,或者可能根本没有任何app.config! :o/. :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. 我还尝试在运行时从ConfigurationManager中访问ServiceModel配置,并尝试通过代码对其进行修改...但是它没有任何内容。

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. 我进行了一些必要的修复,以使ChannelFactory正常工作,并对其进行了修改,以便可以选择是否要使用自己的配置文件进行覆盖,还可以支持地址的覆盖。

I used this constructor within the ExceptionHandlingProxyBase to create the factory: 我在ExceptionHandlingProxyBase中使用了此构造函数来创建工厂:

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. 您可以无视此解决方案的ExceptionHandlingProxyBase部分...只是在通道出现故障时重新建立通道的糖,因此您不必担心代理的状态。

If you still don't want to use a ChannelFactory, then you could try hacking the ServiceModel configuration in your AppDomain. 如果仍然不想使用ChannelFactory,则可以尝试修改AppDomain中的ServiceModel配置。 I tried that, but it seemed to be hard to modify 我尝试过,但是似乎很难修改

Here is the ChannelFactory code with the fixes in (stupidly renamed to CustomClientChannel). 这是带有修复程序的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);
    }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM