繁体   English   中英

C# WCF 关闭通道和使用函数 Func<t></t>

[英]C# WCF closing channels and using functions Func<T>

这就是重点,我有一个 WCF 服务,它现在正在工作。 所以我开始在客户端工作。 当应用程序运行时,出现异常:超时。 所以我开始阅读,有很多关于如何保持连接活跃的例子,但是,我也发现最好的方法是创建通道,使用它,然后处理它。 老实说,我喜欢这样。 所以,现在阅读关闭频道的最佳方法,有两个链接可能对任何需要它们的人有用:

1.清理客户,正确的方式

2. 使用函数

在第一个链接中,这是示例:

    IIdentityService _identitySvc;
...
if (_identitySvc != null)
 {
     ((IClientChannel)_identitySvc).Close();
     ((IDisposable)_identitySvc).Dispose();
     _identitySvc = null;
 }

所以,如果通道不是null,则关闭,处理,分配null。 但我有一个小问题。 在这个例子中,通道有一个.Close() 方法,但是,在我的例子中,智能感知没有显示一个 Close() 方法。 它仅存在于工厂 object 中。 所以我相信我必须写它。 但是,在具有合同的界面或实现它的 class 的界面中? 而且,这个方法应该怎么做???。

现在,下一个链接,这有一些我以前没有尝试过的东西。 Func<T> 而且看了目标之后,还蛮有意思的。 它创建了一个函数,使用 lambdas 创建通道、使用它、关闭它并丢弃它。 这个例子实现了 function 就像一个Using()语句。 这真的很好,而且是一个很好的改进。 但是,我需要一点帮助,老实说,我无法理解 function,所以,专家的一点解释将非常有用。 这是 function:

TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code)
{
    var chanFactory = GetCachedFactory<TChannel>();
    TChannel channel = chanFactory.CreateChannel();
    bool error = true; 
    try {
        TReturn result = code(channel); 
        ((IClientChannel)channel).Close();
        error = false; 
        return result; 
    }
    finally {
        if (error) {
            ((IClientChannel)channel).Abort();
        }
    }
}

这就是使用方式:

int a = 1; 
int b = 2; 
int sum = UseService((ICalculator calc) => calc.Add(a, b)); 
Console.WriteLine(sum);

是的,我认为它真的非常好,我想了解它以便在我的项目中使用它。

而且,像往常一样,我希望这对很多人都有帮助。

UseService 方法接受一个委托,该委托使用通道发送请求。 委托有一个参数和一个返回值。 您可以在委托中调用 WCF 服务。

在 UseService 中,它会创建通道并将通道传递给应由您提供的委托。 通话结束后,关闭频道。

代理 object 不仅实现了您的合约 - 它还实现了允许控制代理生命周期的IClientChannel

第一个示例中的代码不可靠 - 如果通道已被破坏(例如,服务在基于 session 的交互中出现故障),它将泄漏。 正如您在第二个版本中看到的那样,如果出现错误,它会在代理上调用 Abort 仍然清理客户端

您也可以使用扩展方法执行此操作,如下所示:

 enum OnError
 {
     Throw,
     DontThrow
 }

 static class ProxyExtensions
 {
     public static void CleanUp(this IClientChannel proxy, OnError errorBehavior)
     {
         try
         {
             proxy.Close();
         }
         catch
         {
             proxy.Abort();

             if (errorBehavior == OnError.Throw)
             {
                 throw;
             }
         }
     }
 }

不过这个的使用有点麻烦

 ((IClientChannel)proxy).CleanUp(OnError.DontThrow);

但是,如果您制作自己的代理接口来扩展您的合同和 IClientChannel,则可以使这更加优雅

interface IPingProxy : IPing, IClientChannel
{

}

要回答 Jason 的回答中评论中留下的问题,GetCachedFactory 的简单示例可能如下所示。 该示例通过在配置文件中查找“Contract”属性等于工厂要创建的服务的 ConfigurationName 的端点来查找要创建的端点。

ChannelFactory<T> GetCachedFactory<T>()
{
    var endPointName = EndPointNameLookUp<T>();
    return new ChannelFactory<T>(endPointName);
}

// Determines the name of the endpoint the factory will create by finding the endpoint in the config file which is the same as the type of the service the factory is to create
string EndPointNameLookUp<T>()
{
    var contractName = LookUpContractName<T>();
    foreach (ChannelEndpointElement serviceElement in ConfigFileEndPoints)
    {
        if (serviceElement.Contract == contractName) return serviceElement.Name;
    }
    return string.Empty;
}

// Retrieves the list of endpoints in the config file
ChannelEndpointElementCollection ConfigFileEndPoints
{
    get
    {
        return ServiceModelSectionGroup.GetSectionGroup(
            ConfigurationManager.OpenExeConfiguration(
                ConfigurationUserLevel.None)).Client.Endpoints;
    }
}

// Retrieves the ConfigurationName of the service being created by the factory
string LookUpContractName<T>()
{
    var attributeNamedArguments = typeof (T).GetCustomAttributesData()
        .Select(x => x.NamedArguments.SingleOrDefault(ConfigurationNameQuery));

    var contractName = attributeNamedArguments.Single(ConfigurationNameQuery).TypedValue.Value.ToString();
    return contractName;
}

Func<CustomAttributeNamedArgument, bool> ConfigurationNameQuery
{
    get { return x => x.MemberInfo != null && x.MemberInfo.Name == "ConfigurationName"; }
}

更好的解决方案是让 IoC 容器为您管理客户端的创建。 例如,使用autofac它会喜欢以下内容。 首先,您需要像这样注册服务:

var builder = new ContainerBuilder();

builder.Register(c => new ChannelFactory<ICalculator>("WSHttpBinding_ICalculator"))
  .SingleInstance();
builder.Register(c => c.Resolve<ChannelFactory<ICalculator>>().CreateChannel())
  .UseWcfSafeRelease();

container = builder.Build();

其中“WSHttpBinding_ICalculator”是配置文件中端点的名称。 然后稍后您可以像这样使用该服务:

using (var lifetime = container.BeginLifetimeScope())
{
    var calc = lifetime.Resolve<IContentService>();
    var sum = calc.Add(a, b);
    Console.WriteLine(sum);
}

暂无
暂无

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

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