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

标签 c# wcf soap xml-serialization soap-client

我当前的实现是利用 ClientBase 类为对第三方 API 进行的 WCF 调用创建 channel 。此第三方 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);
        }
    }
}

客户端实例化和请求可以在这里看到:

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);

不幸的是,为 WCF 调用创建 channel 的过程需要超过 9 秒才能完成。使用 ReSharper 的 doTrace 探查器,我能够看到代码在以下方法中被阻止:System.ServiceModel.Description.XmlSerializer.OperationBehavior+Reflecto.EnsureMessageInfos

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

我已经尝试使用 sgen.exe 创建一个 XML 序列化程序集,希望它能提高序列化程序的性能。不幸的是,它没有效果。

我还在网上找到了几种建议缓存 channel 或 channel 工厂的方法,例如此处 http://www.itprotoday.com/microsoft-visual-studio/wcf-proxies-cache-or-not-cache .但是,这些方法不适用于此实现,因为 Channel Factory 具有与之关联的客户端凭据。这将需要为每个客户端缓存一个 Channel Factory 或 Channel,这是不现实的。

有谁知道一种方法可以防止 ChannelFactory 在创建时反射(reflect)在 Request 和 Response 对象上?任何人都可以在此问题上提供的任何帮助将不胜感激。

最佳答案

我不知道有什么机制可以让您绕过您在此处看到的行为。这本质上就是 ChannelFactory 的设计方式:它承担了反射和组合 channel 堆栈的大量一次性成本,为您提供了一个廉价的例程来创建 channel 实例。如果您想节省 9 秒,您必须重新使用工厂。

通常我会建议使用 built-in caching与客户端实例相关联的 ChannelFactory,但是当您触摸 ClientCredentials 属性时,它就会失效。

我建议您确实确实需要考虑在每个客户端的基础上缓存每个ChannelFactory。除非您拥有数万套凭证,否则这并非不切实际的前景。实际上,这就是 .NET 中的 HTTP 系统用于预授权请求的方式。

关于c# - 如何在 System.ServiceModel.ChannelFactory 中优化 WCF CreateFactory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50394490/

相关文章:

c# - 使用空分配修复 'Use of unassigned local variable'。为什么?

c# - MVC 6 中检测到的 Controller 类列表

c# - IIS 中托管的 WCF 服务在不同线程中使用时返回 "Safe handle has been closed"

java - 如何使用 Java 解析来自第三方服务器的 SOAP 响应

c# - 将 string[] 转换为 Int[] 而不丢失前导零

c# - 为什么将基类设置为等于派生类型仅在设置范围内起作用?

c# - SOAP header 中的 WCF 用户名和密码

c# - 做数据库操作时线程是否阻塞?

node.js - Node js SOAP 库与 DateAdvance 一词相关的问题

javascript - 将 Java SOAP 请求转换为 Javascript?