简体   繁体   English

如何在System.ServiceModel.ChannelFactory中优化WCF CreateFactory?

[英]How to optimize WCF CreateFactory in System.ServiceModel.ChannelFactory?

My current implementation is utilizing the ClientBase class to create a channel for WCF calls made to a third party API. 我当前的实现是利用ClientBase类为第三方API的WCF调用创建一个通道。 This third party API requires a X509Certificate2 certificate as well as ClientCredentials to be authenticated. 此第三方API需要对X509Certificate2证书以及ClientCredentials进行身份验证。

public class HeaderAdder : ContextBoundObject, IClientMessageInspector
{
    public bool RequestFailedDueToAuthentication;

    public string UserName { get; set; }
    public string Password { get; set; }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        var property = new UserNameHeader
        {
            Password = Password,
            UserName = UserName
        };
        request.Headers.Add(MessageHeader.CreateHeader("UserNameHeader", "test", property));
        return null;
    }

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        RequestFailedDueToAuthentication = reply.ToString().Contains("ErrorCode>-4<");
    }
}

public class CustomEndpointBehavior : IEndpointBehavior
{
    private readonly HeaderAdder _headerAdder;

    public CustomEndpointBehavior(HeaderAdder headerAdder)
    {
        _headerAdder = headerAdder;
    }

    public void Validate(ServiceEndpoint endpoint)
    {
        //throw new NotImplementedException();
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        //throw new NotImplementedException();
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        //throw new NotImplementedException();
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        var credentials = endpoint.Behaviors.Find<ClientCredentials>();
        if (!string.IsNullOrEmpty(credentials.UserName.Password))
        {
            _headerAdder.UserName = credentials.UserName.UserName;
            _headerAdder.Password = credentials.UserName.Password;
            clientRuntime.ClientMessageInspectors.Add(_headerAdder);
        }
    }
}

Client instantiation and request can be seen here: 客户端实例化和请求可以在这里看到:

var client = new TestClient()
{
    ClientCredentials =
    {
        UserName =
        {
            UserName = "testing",
            Password = "testing"
        },
        UseIdentityConfiguration = true
    }
};
client.ClientCredentials?.ClientCertificate.SetCertificate(
    StoreLocation.LocalMachine, 
    StoreName.My,
    X509FindType.FindByIssuerName, 
    "Testing");
client.ChannelFactory.Endpoint.EndpointBehaviors.Add(
   new CustomEndpointBehavior(new HeaderAdder()));
var request = new Request();
client.Get(request);

Unfortunately the process of creating a Channel for the WCF call takes over 9 seconds to complete. 不幸的是,为WCF调用创建Channel的过程需要9秒以上才能完成。 Using ReSharper's doTrace profiler I am able to see that the code is being held up on the following method: System.ServiceModel.Description.XmlSerializer.OperationBehavior+Reflecto.EnsureMessageInfos 使用ReSharper的doTrace探查器,我能够看到代码在以下方法上被阻止: System.ServiceModel.Description.XmlSerializer.OperationBehavior + Reflecto.EnsureMessageInfos

A full stack trace of the calls being made in System.ServiceModel can be seen below. 可以在下面看到在System.ServiceModel中进行的调用的完整堆栈跟踪。

System.ServiceModel.ClientBase`1.get_Channel
System.ServiceModel.ClientBase`1.CreateChannelInternal
System.ServiceModel.ClientBase`1.CreateChannel
System.ServiceModel.ChannelFactory`1.CreateChannel
System.ServiceModel.ChannelFactory`1.CreateChannel(EndpointAddress, Uri)
System.ServiceModel.ChannelFactory.EnsureOpened
System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan)
System.ServiceModel.ChannelFactory.OnOpening
System.ServiceModel.ChannelFactory.CreateFactory
System.ServiceModel.Channels.ServiceChannelFactory.BuildChannelFactory(ServiceEndpoint, Boolean)
System.ServiceModel.Description.DispatcherBuilder.BuildProxyBehavior(ServiceEndpoint, out BindingParameterCollection)
System.ServiceModel.Description.DispatcherBuilder.ApplyClientBehavior(ServiceEndpoint, ClientRuntime)
System.ServiceModel.Description.DispatcherBuilder.BindOperations(ContractDescription, ClientRuntime, DispatchRuntime)
System.ServiceModel.Description.XmlSerializerOperationBehavior.ApplyClientBehavior(OperationDescription, ClientOperation)
System.ServiceModel.Description.XmlSerializerOperationBehavior.CreateFormatter
System.ServiceModel.Description.XmlSerializerOperationBehavior+Reflector.EnsureMessageInfos

I have already tried using sgen.exe to create an XML serialization assembly in hopes that it would improve the serializer's performance. 我已经尝试使用sgen.exe来创建XML序列化程序集,希望它能提高序列化程序的性能。 Unfortunately, it had no effect. 不幸的是,它没有任何效果。

I have also found several approaches online that recommend caching Channels, or Channel Factories such as here http://www.itprotoday.com/microsoft-visual-studio/wcf-proxies-cache-or-not-cache . 我还在网上找到了几种建议缓存频道或渠道工厂的方法,例如http://www.itprotoday.com/microsoft-visual-studio/wcf-proxies-cache-or-not-cache However, these approaches do not work for this implementation because the Channel Factory has Client Credentials associated with it. 但是,这些方法不适用于此实现,因为Channel Factory具有与之关联的客户端凭据。 This would require caching of a Channel Factory or Channel for each client which is unrealistic. 这将需要为每个客户端缓存一个Channel Factory或Channel,这是不现实的。

Does anyone know of a way to prevent the ChannelFactory from reflecting over the Request and Response objects when it is created? 有没有人知道一种方法来阻止ChannelFactory在创建时反映请求和响应对象? Any assistance anyone can be provide on this issue would greatly appreciated. 任何人都可以在这个问题上提供的任何帮助将非常感谢。

I don't know of any mechanism that will let you bypass the behaviour you're seeing here. 我不知道任何机制会让你绕过你在这里看到的行为。 This is intrinsically how ChannelFactory was designed: it does the heavy one-off costs of reflection and composing the channel stack to give you a cheap routine to create channel instances. 这本质上是ChannelFactory设计方式:它可以完成一次性的反射和组合通道堆栈,为您提供创建通道实例的便宜例程。 You must re-use the factory if you want to save your 9 seconds. 如果要保存9秒钟, 必须重新使用工厂。

Normally I'd suggest using the built-in caching of the ChannelFactory associated with client instances, but this is invalidated the moment you touch the ClientCredentials property. 通常我建议使用与客户端实例关联的ChannelFactory内置缓存 ,但是当您触摸ClientCredentials属性时,这将无效。

I would suggest that you really do need to consider caching each ChannelFactory on a per-client basis. 我建议你真的需要考虑缓存每个ChannelFactory在每个客户端的基础。 Unless you have literal tens of thousands of sets of credentials, it's not an unrealistic prospect. 除非你有数以万计的证书,否则这不是一个不切实际的前景。 Indeed, this is how the HTTP systems in .NET work to pre-authorise requests. 实际上,这就是.NET中的HTTP系统如何预先授权请求。

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

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