WCF 项目应用连载[5] - 自定义配置 扩展ChannelFactory<T> - LDuplex<T>

本文介绍如何扩展WCF的ChannelFactory<T>,详细讲解了客户端配置,包括绑定、行为等内容,并阐述了重写CreateDescription方法以加入client、bindings和behaviors信息的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面我们扩展了ServiceHost,这节我们将扩展ChannelFactory

扩展2个类


public class LChannelFactory<T> : ChannelFactory<T>
public class LDuplex<T> : DuplexChannelFactory<T>


由于 DuplexChannelFactory<T>继承了 ChannelFactory<T>
本节我们以扩展DuplexChannelFactory<T>为例进行说明。

当然,别担心,这两个类的扩展我们都会提供源代码。

一个LDuplex<T> + LChannelFactory<T>


5.1 客户端配置文件 App.config



1)要真正讲扩展ChannelFactory之前,我还还是来了解下,我们ChannelFactory扩展操作了哪些参数。所以,与ServiceHost一样,先了解配置。

2)这儿也是一个App.config
在一定程度上,WCF是依赖于配置的编程,即便我们可以以编码的方式添加WCF运行参数。
如图5.1.1所示。

客户端配置非常简单,甚至连行为配置behaviorConfiguration也可为空。但此处我们为了完整的说明客户商端配置。我们采用图 5.1.2去解释客户端完整的配置。



5.1.1客户端配置



5.2 完整的客户端配置文件 App.config



客户端配置由三部分关键信息组成:
Client : 含endpoint节点的客户端通信信息
Bindings: 绑定参数。主要是TCP通信控制参数,为client中的endpoint信息服务。
Behaviors: 行为。为client中的endpoint信息服务。

还记不记得我们的服务端的配置

服务 – 绑定 – 行为

客户 – 绑定 – 行为




有了这样的对比,只是这儿从服务变成了客户。我们基本清楚了ChannelFactory要做一些什么工作。
你可能猜测,这个ChannelFactory类是不是也得有一个

protected virtual void ApplyConfiguration();

方法来对配置进行操作,而我们只需要重写ApplyConfiguration就可以了?

你猜得没错。的确。ChannelFactory类有这样的方法存在:
protected virtual void ApplyConfiguration(string configurationName);

不过,我们在 ChannelFactory 中的确要重写基类的这个方法,但还要重写另外一个方法:
protected override ServiceEndpoint CreateDescription();

这是因为,可能有很多endpoint,我们需要将每一个endpoint信息加入到 ServiceEndpoint


5.2.1客户端完整配置

现在。我们的问题清楚了。我们要重写CreateDescription方法,并加入client信息,bindings信息与behaviors信息。这样我们将达成最初的设想。

回过头来,看前面提到的

public class LChannelFactory<T> : ChannelFactory<T>
public class LDuplex<T> : DuplexChannelFactory<T>


public class LChannelFactory<T> : ChannelFactory<T>
public class LDuplex<T> : DuplexChannelFactory<T>


你的想法是对了。这儿一个用来单向通信,DuplexChannelFactory则是双向通信的。

在这节中,我们以DuplexChannelFactory为例说明如何重写CreateDescription这个方法。这儿比ServiceHost重写ApplyConfiguration稍复杂,因为CreateDescription内部有很多工作要做。


1)重写CreateDescription

我们关心的是这个函数做了哪些工作?
[1] 更改配置路径
[2]添加endpoint信息
[3]创建bindings与添加客户端行为

/// <summary>
        /// Override CreateDescription and applyConfiguration
        /// </summary>
        /// <returns></returns>
        protected override ServiceEndpoint CreateDescription()
        {
            // Set path!!!!!!!!!!!!!!!!
            this.configpath = ConfigInfo;
            this.endpointCfgname = ServiceInfo;

            if (!CheckConfigExist(this.configpath))
            {
                return base.CreateDescription();
            }
            if (string.IsNullOrEmpty(this.endpointCfgname))
            {
                return base.CreateDescription();
            }
            lock (lockObj)
            {
                try
                {
                    // Build service endpoint info
                    ServiceEndpoint servicep = new ServiceEndpoint(ContractDescription.GetContract(typeof(T)));
                    // Set serviceEndpoint name as config
                    //servicep.Name = this.endpointCfgname;

                    // Set user config
                    ExeConfigurationFileMap execfgMap = new ExeConfigurationFileMap();
                    execfgMap.ExeConfigFilename = this.configpath;

                    // Get config infomation
                    Configuration cfg = ConfigurationManager.OpenMappedExeConfiguration(execfgMap, ConfigurationUserLevel.None);

                    // Get all service model sections info
                    ServiceModelSectionGroup servicemodelSections = ServiceModelSectionGroup.GetSectionGroup(cfg);

                    // tmp servicep.Name
                    string tmpnameforep = null;

                    // Get Endpoint element info
                    ChannelEndpointElement selectedep = GetEndpointElement(servicemodelSections,
                        servicep.Contract.ConfigurationName, out tmpnameforep);

                    // Get completed servicep info
                    bool isAddAll = GetServicepCompletedInfo(servicep, servicemodelSections, selectedep);

                    // Set servicep.Name the right info
                    if (!string.IsNullOrEmpty(tmpnameforep)) servicep.Name = tmpnameforep;

                    this.epr = servicep.Address;
                    return servicep;
                }
                catch (Exception ex)
                {
                    throw new Exception("Error : CreateDescription exception ocurred! Apply user config failed!" + ex.ToString());
                }
            }
        }

2)获取Endpoint信息

/// <summary>
        /// Get EndpointElement info
        /// </summary>
        /// <param name="servicemodelSections"></param>
        /// <param name="contractName"></param>
        /// <param name="epName"></param>
        /// <returns></returns>
        private ChannelEndpointElement GetEndpointElement(ServiceModelSectionGroup servicemodelSections,
            string contractName, out string epName)
        {
            epName = null;
            if (servicemodelSections == null) return null;
            if (string.IsNullOrEmpty(contractName)) return null;

            ChannelEndpointElement epElement = null;
            // Element flag
            bool isExisted = false;
            foreach (ChannelEndpointElement element in servicemodelSections.Client.Endpoints)
            {
                if (element.Contract == contractName &&
                    this.endpointCfgname == element.Name)
                {
                    // Get element info
                    epElement = element;
                    // Get service name
                    epName = element.Name;
                    // Find successfully
                    isExisted = true;
                    break;
                }
            }
            if (!isExisted) return null;
            return epElement;
        }

3)添加行为

/// <summary>
        /// 
        /// </summary>
        /// <param name="servicep"></param>
        /// <param name="servicemodelSections"></param>
        /// <param name="eplement"></param>
        /// <returns></returns>
        private bool GetServicepCompletedInfo(ServiceEndpoint servicep,
            ServiceModelSectionGroup servicemodelSections, ChannelEndpointElement eplement)
        {
            if (eplement != null)
            {
                if (servicep.Binding == null)
                {
                    servicep.Binding = CreateBinding(eplement.Binding, servicemodelSections);
                    if (servicep.Binding != null) this.isAddBinding = true;
                }
                if (servicep.Address == null)
                {
                    servicep.Address = new EndpointAddress(eplement.Address, GetIdentityForVivitue(eplement.Identity), eplement.Headers.Headers);
                    if (servicep.Address != null) this.isAddAddr = true;
                }
                if (servicep.Behaviors.Count == 0 && eplement.BehaviorConfiguration != null)
                {
                    //
                    bool isAdd = false;
                    isAdd = AddBehaviors(eplement.BehaviorConfiguration, servicep, servicemodelSections);
                    if (isAdd) this.isAddBehaviors = true;
                }
            }
            if (this.isAddBinding && this.isAddAddr && this.isAddBehaviors) return true;
            return false;
        }

/// <summary>
        /// Add behaviors info
        /// </summary>
        /// <param name="behaviorCfg"></param>
        /// <param name="servicep"></param>
        /// <param name="modelGroup"></param>
        /// <returns></returns>
        private bool AddBehaviors(string behaviorCfg, ServiceEndpoint servicep, ServiceModelSectionGroup modelGroup)
        {
            if (string.IsNullOrEmpty(behaviorCfg)) return false;
            if (servicep == null || modelGroup == null) return false;
            try
            {
                EndpointBehaviorElement behaviorElement = modelGroup.Behaviors.EndpointBehaviors[behaviorCfg];
                for (int i = 0; i < behaviorElement.Count; i++)
                {
                    BehaviorExtensionElement behaviorEx = behaviorElement;
                    object extention = behaviorEx.GetType().InvokeMember("CreateBehavior",
                        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
                        null,
                        behaviorEx,
                        null);
                    if (extention == null) continue;
                    servicep.Behaviors.Add((IEndpointBehavior)extention);
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Warning : Exception ocurred! Behaviors info added failed!  " + ex.ToString());
                return false;
            }
            if (servicep.Behaviors.Count == 0) return false;
            return true;
        }

4)创建bindings

/// <summary>
        /// Get Binding info
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        private Binding GetBindingInfo(IBindingConfigurationElement element)
        {
            if (element == null) return null;

            if (element is CustomBindingElement) return new CustomBinding();
            else if (element is BasicHttpBindingElement) return new BasicHttpBinding();
            else if (element is NetMsmqBindingElement) return new NetMsmqBinding();
            else if (element is NetNamedPipeBindingElement) return new NetMsmqBinding();
            else if (element is NetTcpBindingElement) return new NetTcpBinding();
            else if (element is WSDualHttpBindingElement) return new WSDualHttpBinding();
            else if (element is WSHttpBindingElement) return new WSHttpBinding();
            else if (element is WSFederationHttpBindingElement) return new WSFederationHttpBinding();
            else return null;
        }

/// <summary>
        /// Get binding info
        /// </summary>
        /// <param name="bindingName"></param>
        /// <param name="modelGroup"></param>
        /// <returns></returns>
        private Binding CreateBinding(string bindingName, ServiceModelSectionGroup modelGroup)
        {
            if (string.IsNullOrEmpty(bindingName)) return null;
            if (modelGroup == null) return null;

            try
            {
                BindingCollectionElement bingdingElement = modelGroup.Bindings[bindingName];
                if (bingdingElement.ConfiguredBindings.Count > 0)
                {
                    IBindingConfigurationElement element = bingdingElement.ConfiguredBindings[0];
                    Binding binding = GetBindingInfo(element);
                    if (binding != null) element.ApplyConfiguration(binding);
                    return binding;
                }
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine("Attention : CreateBinding failed! " + ex.ToString());
            }
            return null;
        }

5) 重写ApplyConfiguration

/// <summary>
        /// Override ApplyConfiguration  - Current doing nothing here
        /// </summary>
        /// <param name="configurationName"></param>
        protected override void ApplyConfiguration(string configurationName)
        {
            //base.ApplyConfiguration
            // nop
        }

6) 获取证书信息
这段代码后面我们讲 x.509安全证书时会说明。


/// <summary>
        /// Get identity
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        private EndpointIdentity GetIdentityForVivitue(IdentityElement element)
        {
            if (element == null) return null;
            EndpointIdentity itentity = null;

            //PropertyInformationCollection properties = element.ElementInformation.Properties;
            if (!string.IsNullOrEmpty(element.UserPrincipalName.Value))
            {
                return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value);
            }
            else if (!string.IsNullOrEmpty(element.ServicePrincipalName.Value))
            {
                return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value);
            }
            else if (!string.IsNullOrEmpty(element.Dns.Value))
            {
                return EndpointIdentity.CreateDnsIdentity(element.Dns.Value);
            }
            else if (!string.IsNullOrEmpty(element.Rsa.Value))
            {
                return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value);
            }
            else if (!string.IsNullOrEmpty(element.Certificate.EncodedValue))
            {
                X509Certificate2Collection supportCerfificates = new X509Certificate2Collection();
                supportCerfificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue));
                if (supportCerfificates.Count == 0)
                {
                    throw new InvalidOperationException("Error : Unable to load certificate identity!");
                }
                X509Certificate2 primaryCertificate = supportCerfificates[0];
                supportCerfificates.RemoveAt(0);
                return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportCerfificates);
            }
            return itentity;
        }

.3 关于LWCF



为了让创建WCF客户端更清单。我们创建了LWCF来封装LDuplex与LChannelFactory

1)服建WCF客户端通道

/// <summary>
        /// 
        /// </summary>
        public void ConnectServer()
        {
            // Check config and service fullname!

            CheckValidityAndThrowException();

            // Clear last time record
            this.linkElapsedTime = 0;
            int beginTime = SysInfo.TickCount;

            if (!linkStatus)
            {
                try
                {
                    channelFactory = GetChannelInstance(this.wcfType);
                    if (channelFactory.State == CommunicationState.Created)
                    {
                        interfaceInstance = channelFactory.CreateChannel();
                        if (channelFactory.State == CommunicationState.Opened)
                        {
                            linkStatus = true;
                        }
                        else
                        {
                            linkStatus = false;
                        }
                    }
                }
                catch (Exception e) 
                {
                    linkStatus = false;
                    throw new Exception(e.Message);
                }
            }
            // Get link time
            this.linkElapsedTime = SysInfo.TickCount - beginTime;
        }


2)获取ChannelFactory<T>实例

/// <summary>
        /// Get channel instance according WCFClientType
        /// </summary>
        /// <param name="type">WCFClientType</param>
        /// <returns></returns>
        private ChannelFactory<T> GetChannelInstance(WCFClientType type)
        {
            WCFClientType wtype = type;
            ChannelFactory<T> channel = null;
            try
            {
                switch (wtype)
                {
                    case WCFClientType.Single:
                        channel = CreateLChannelFactory();
                        break;
                    case WCFClientType.Duplex:
                        channel = CreateDuplexChannel();
                        break;
                    case WCFClientType.Default:
                        channel = CreateComchannel();
                        break;
                    default:
                        channel = CreateComchannel();
                        break;
                }
            }
            catch (Exception ex)
            {
                channel = null;
                Innerlig.Error(dclringType, "GetChannelInstance failed!", ex);
            }
            return channel;
        }

3)获取WCF客户端实例
/// <summary>
        /// Get client instance
        /// </summary>
        /// <returns></returns>
        public T GetInstance()
        {
            return interfaceInstance;
        }



好了。到目前为止我们研究了WCF的配置文件,做了大量工作。完成了对ServiceHost的扩展与对ChannelFactory的扩展。形成了我们自己最终用的两个类:


public class LWCF<T>

public class LServiceHost : ServiceHost


接下来,我们将用这两个类来升级我们的Lig服务。让Lig变成一个更加完美的版本。。。。。。。


本节源代码下载:
————————————————————————————————————————————————

LChannelFactory.cs
LDuplex.cs
LWCF.cs

————————————————————————————————————————————————

图5.1.2中的的行为配置我们在后面讲x.509安全证书时会说到这段有用的客户端配置

behaviorConfiguration="IgoreSvcCertValidation"


<behaviors>
      <endpointBehaviors>
        <behavior name="IgoreSvcCertValidation">
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="None"/>
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
 



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值