前面我们扩展了ServiceHost,这节我们将扩展ChannelFactory
由于 DuplexChannelFactory<T>继承了 ChannelFactory<T>
本节我们以扩展DuplexChannelFactory<T>为例进行说明。
当然,别担心,这两个类的扩展我们都会提供源代码。
一个LDuplex<T> + LChannelFactory<T>
5.1 客户端配置文件 App.config
在一定程度上,WCF是依赖于配置的编程,即便我们可以以编码的方式添加WCF运行参数。
如图5.1.1所示。
5.2 完整的客户端配置文件 App.config
客户端配置由三部分关键信息组成:
Client : 含endpoint节点的客户端通信信息
Bindings: 绑定参数。主要是TCP通信控制参数,为client中的endpoint信息服务。
Behaviors: 行为。为client中的endpoint信息服务。
还记不记得我们的服务端的配置
服务 – 绑定 – 行为
有了这样的对比,只是这儿从服务变成了客户。我们基本清楚了ChannelFactory要做一些什么工作。
你可能猜测,这个ChannelFactory类是不是也得有一个
方法来对配置进行操作,而我们只需要重写ApplyConfiguration就可以了?
你猜得没错。的确。ChannelFactory类有这样的方法存在:
不过,我们在 ChannelFactory 中的确要重写基类的这个方法,但还要重写另外一个方法:
这是因为,可能有很多endpoint,我们需要将每一个endpoint信息加入到 ServiceEndpoint中
你的想法是对了。这儿一个用来单向通信,DuplexChannelFactory则是双向通信的。
在这节中,我们以DuplexChannelFactory为例说明如何重写CreateDescription这个方法。这儿比ServiceHost重写ApplyConfiguration稍复杂,因为CreateDescription内部有很多工作要做。
1)重写CreateDescription
我们关心的是这个函数做了哪些工作?
[1] 更改配置路径
[2]添加endpoint信息
[3]创建bindings与添加客户端行为
2)获取Endpoint信息
3)添加行为
4)创建bindings
5) 重写ApplyConfiguration
6) 获取证书信息
这段代码后面我们讲 x.509安全证书时会说明。
.3 关于LWCF
为了让创建WCF客户端更清单。我们创建了LWCF来封装LDuplex与LChannelFactory
1)服建WCF客户端通道
3)获取WCF客户端实例
好了。到目前为止我们研究了WCF的配置文件,做了大量工作。完成了对ServiceHost的扩展与对ChannelFactory的扩展。形成了我们自己最终用的两个类:
本节源代码下载:
————————————————————————————————————————————————
LChannelFactory.cs
LDuplex.cs
LWCF.cs
————————————————————————————————————————————————
扩展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
本节源代码下载:
————————————————————————————————————————————————
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>