WCF 4.0 服务发现 - 将绑定添加到发现






4.91/5 (4投票s)
如何将绑定信息包含在 WCF 4.0 发现中
引言
WCF 4.0 增加了一个很棒的新功能。 WCF 4.0 服务发现允许我们查看正在运行的服务及其位置。 但是,当我开始详细研究此功能时,我注意到您在服务上获得的信息仅包含 ABC 的 A (地址) 和 C (契约) - 地址和契约,但不包含绑定。 因此,在本文中,我将展示如何轻松地将绑定添加到发现信息中。 注意:所有代码都是在 Visual Studio 2010 RC 上开发的。
EndpointDiscoveryMetadata 类
在发现中,此类可能是最重要的类之一。 这是我们在服务启动和停止时获得的信息。 您可以在下面看到,没有关于服务的绑定信息。 请注意,您可以使用以下方法获取联系人姓名:
Collection<xmlqualifiedname> ContractTypeNames { get; }
并且您可以使用以下方法获取地址:
public EndpointAddress Address { get; set; }
但是,没有绑定信息! 当我在论坛上发帖询问为什么这样做时,他们告诉我绑定不是发现标准 (WS Discovery) 的一部分,他们还告诉我,您可以通过简单地发现 Mex 终结点然后调用获取服务的元数据来毫无问题地获取绑定。 但是,这并不理想,您需要发现服务,然后获取其元数据才能知道终结点的完整 ABC(2 次网络往返)。 (http://social.msdn.microsoft.com/Forums/en/wcfprerelease/thread/ca6bc4be-bc46-4740-ba1e-dca8cf39aa5f) EndpointDiscoveryMetadata
的一个关键字段是 Extensions
。 因此,我立刻决定看看如何填充这些扩展并在其中添加我自己的自定义信息。 在这种情况下,它将是绑定信息。
代码
下面的示例基于 Microsoft 的 WCF / WF 代码示例。 该代码已被修改,以将绑定信息添加到发现元数据中。 我已将服务代理示例用于此原型。 您可以从 此处 获取 Microsoft 示例代码。
将扩展添加到 EndpointDiscoveryMetadata
要将扩展添加到发现元数据,您需要创建一个新的 Endpoint Behavior 并将其添加到 Endpoint.Behaviors
集合中。 该行为是属于发现子系统的特殊行为,称为 EndpointDiscoveryBehavior
。 创建此行为后,您可以使用 Extension 集合将其添加到其中。
endpointDiscoveryBehavior.Extensions.Add
为了确保绑定信息附加到每个终结点的发现元数据,最好的方法是创建一个新的 ServiceBehavior
,并将 EndpointDiscoveryBehavior
添加到每个终结点。 下面是 Service Behavior 的代码
public class BindingDiscoveryServiceBehavior : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase,
System.Collections.ObjectModel.Collection<serviceendpoint> endpoints,
BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
var endpoints = serviceDescription.Endpoints;
foreach (ServiceEndpoint endpoint in endpoints)
{
var endpointDiscoveryBehavior = new EndpointDiscoveryBehavior();
StringBuilder sb = new StringBuilder();
sb.Append(endpoint.Address);
sb.Append(Environment.NewLine);
sb.Append(endpoint.Binding.Scheme);
sb.Append(Environment.NewLine);
sb.Append(endpoint.Binding.Name);
string bindingInfo = sb.ToString();
string largeData = String.Empty;
StringBuilder sb2 = new StringBuilder();
for (int i = 0; i < 3000000; i++)
sb2.Append("Lots of data " + i.ToString() + Environment.NewLine);
largeData = sb2.ToString();
// add the binding information to the endpoint
endpointDiscoveryBehavior.Extensions.Add(
new XElement(
"root",
new XElement("BindingData", bindingInfo),
new XElement("LargeData", largeData)));
// add the extension
endpoint.Behaviors.Add(endpointDiscoveryBehavior);
}
}
public void Validate(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
}
}
请注意,我还向扩展中添加了非常大的数据,只是为了表明您可以在 Extensions
中传递大量数据(但您需要在发现代理 TCP 绑定的限制上进行增加)。 下一步很简单,您只需要将您的 Service Behavior 添加到您的服务中。 您可以通过配置来执行此操作,或者仅将其作为属性应用于服务
[BindingDiscoveryServiceBehavior]
public class CalculatorService : ICalculatorService
当服务被“发现”时获取信息
为了在发现服务时查看绑定信息,我修改了 WCF 发现示例中的服务代理:当发现服务时,将调用 OnBeginOnlineAnnouncement
。 下面是修改后的实现
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class DiscoveryProxyService : DiscoveryProxy
{
// Repository to store EndpointDiscoveryMetadata.
// A database or a flat file could also be used instead.
Dictionary<endpointaddress,> onlineServices;
public DiscoveryProxyService()
{
this.onlineServices = new Dictionary<endpointaddress,>();
}
// OnBeginOnlineAnnouncement method is called when a Hello message
// is received by the Proxy
protected override IAsyncResult OnBeginOnlineAnnouncement
(DiscoveryMessageSequence messageSequence,
EndpointDiscoveryMetadata endpointDiscoveryMetadata,
AsyncCallback callback, object state)
{
this.AddOnlineService(endpointDiscoveryMetadata);
return new OnOnlineAnnouncementAsyncResult(callback, state);
}
protected override void OnEndOnlineAnnouncement(IAsyncResult result)
{
OnOnlineAnnouncementAsyncResult.End(result);
}
// OnBeginOfflineAnnouncement method is called when a
// Bye message is received by the Proxy
protected override IAsyncResult OnBeginOfflineAnnouncement
(DiscoveryMessageSequence messageSequence,
EndpointDiscoveryMetadata endpointDiscoveryMetadata,
AsyncCallback callback, object state)
{
this.RemoveOnlineService(endpointDiscoveryMetadata);
return new OnOfflineAnnouncementAsyncResult(callback, state);
}
protected override void OnEndOfflineAnnouncement(IAsyncResult result)
{
OnOfflineAnnouncementAsyncResult.End(result);
}
// OnBeginFind method is called when a Probe request message is received by the Proxy
protected override IAsyncResult OnBeginFind(FindRequestContext findRequestContext,
AsyncCallback callback, object state)
{
this.MatchFromOnlineService(findRequestContext);
return new OnFindAsyncResult(callback, state);
}
protected override void OnEndFind(IAsyncResult result)
{
OnFindAsyncResult.End(result);
}
// OnBeginFind method is called when a Resolve
// request message is received by the Proxy
protected override IAsyncResult OnBeginResolve
(ResolveCriteria resolveCriteria, AsyncCallback callback, object state)
{
return new OnResolveAsyncResult
(this.MatchFromOnlineService(resolveCriteria), callback, state);
}
protected override EndpointDiscoveryMetadata OnEndResolve(IAsyncResult result)
{
return OnResolveAsyncResult.End(result);
}
// The following are helper methods required by the Proxy implementation
void AddOnlineService(EndpointDiscoveryMetadata endpointDiscoveryMetadata)
{
lock (this.onlineServices)
{
this.onlineServices[endpointDiscoveryMetadata.Address] =
endpointDiscoveryMetadata;
}
PrintDiscoveryMetadata(endpointDiscoveryMetadata, "Adding");
// show the binding information
PrintBindingInformation(endpointDiscoveryMetadata);
}
private void PrintBindingInformation
(EndpointDiscoveryMetadata endpointDiscoveryMetadata)
{
// Get the binding data
XElement element = endpointDiscoveryMetadata.Extensions.Elements
("BindingData").FirstOrDefault();
string bindingInfo = element.Value;
Console.WriteLine("Binding Data");
Console.WriteLine(bindingInfo);
}
void RemoveOnlineService(EndpointDiscoveryMetadata endpointDiscoveryMetadata)
{
if (endpointDiscoveryMetadata != null)
{
lock (this.onlineServices)
{
this.onlineServices.Remove(endpointDiscoveryMetadata.Address);
}
PrintDiscoveryMetadata(endpointDiscoveryMetadata, "Removing");
}
}
void MatchFromOnlineService(FindRequestContext findRequestContext)
{
lock (this.onlineServices)
{
foreach (EndpointDiscoveryMetadata
endpointDiscoveryMetadata in this.onlineServices.Values)
{
if (findRequestContext.Criteria.IsMatch(endpointDiscoveryMetadata))
{
findRequestContext.AddMatchingEndpoint(endpointDiscoveryMetadata);
}
}
}
}
EndpointDiscoveryMetadata MatchFromOnlineService(ResolveCriteria criteria)
{
EndpointDiscoveryMetadata matchingEndpoint = null;
lock (this.onlineServices)
{
foreach (EndpointDiscoveryMetadata
endpointDiscoveryMetadata in this.onlineServices.Values)
{
if (criteria.Address == endpointDiscoveryMetadata.Address)
{
matchingEndpoint = endpointDiscoveryMetadata;
}
}
}
return matchingEndpoint;
}
void PrintDiscoveryMetadata(EndpointDiscoveryMetadata endpointDiscoveryMetadata,
string verb)
{
Console.WriteLine("\n**** " + verb +
" service of the following type from cache. ");
foreach (XmlQualifiedName contractName in
endpointDiscoveryMetadata.ContractTypeNames)
{
Console.WriteLine("** " + contractName.ToString());
break;
}
Console.WriteLine("**** Operation Completed");
}
sealed class OnOnlineAnnouncementAsyncResult : AsyncResult
{
public OnOnlineAnnouncementAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
this.Complete(true);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<ononlineannouncementasyncresult>(result);
}
}
sealed class OnOfflineAnnouncementAsyncResult : AsyncResult
{
public OnOfflineAnnouncementAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
this.Complete(true);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<onofflineannouncementasyncresult>(result);
}
}
sealed class OnFindAsyncResult : AsyncResult
{
public OnFindAsyncResult(AsyncCallback callback, object state)
: base(callback, state)
{
this.Complete(true);
}
public static void End(IAsyncResult result)
{
AsyncResult.End<onfindasyncresult>(result);
}
}
sealed class OnResolveAsyncResult : AsyncResult
{
EndpointDiscoveryMetadata matchingEndpoint;
public OnResolveAsyncResult(EndpointDiscoveryMetadata matchingEndpoint,
AsyncCallback callback, object state)
: base(callback, state)
{
this.matchingEndpoint = matchingEndpoint;
this.Complete(true);
}
public static EndpointDiscoveryMetadata End(IAsyncResult result)
{
OnResolveAsyncResult thisPtr = AsyncResult.End<onresolveasyncresult>(result);
return thisPtr.matchingEndpoint;
}
}
}
请注意,我正在下面的代码中获取绑定信息
XElement element = endpointDiscoveryMetadata.Extensions.Elements
("BindingData").FirstOrDefault();
string bindingInfo = element.Value;
结论
您应该考虑使用 WsdlExporter
/WsdlImporter
来序列化终结点中的信息,而不是尝试将其序列化为自定义类,就像我在这里做的那样。 我的目的是展示如何传递“数据”,而此数据可以是您想要的任何内容。 就我而言,我选择传递绑定信息。 现在我有了绑定信息,因此当我请求特定契约时,我可以获得绑定以及地址返回给请求终结点信息的客户端。 现在我们可以发现完整的 A B C,而不仅仅是 A 和 C。