4

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

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

2. 使用函数

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

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

因此,如果通道不为 null,则关闭、处置并分配 null。但我有一个小问题。在此示例中,通道具有 .Close() 方法,但在我的情况下,智能感知没有显示 Close() 方法。它只存在于工厂对象中。所以我相信我必须写它。但是,在具有合同的接口或实现它的类中?而且,这个方法应该怎么做???。

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

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

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

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

4

3 回答 3

3

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

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

于 2011-07-18T04:25:05.833 回答
2

代理对象实现的不仅仅是你的合约——它还实现了允许控制代理生命周期的IClientChannel

第一个示例中的代码不可靠 - 如果通道已被破坏(例如,服务在基于会话的交互中出现故障),它将泄漏。正如您在第二个版本中看到的那样,如果出现错误,它会在代理上调用 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
{

}
于 2011-07-18T07:03:56.323 回答
1

要回答 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);
}
于 2011-12-30T23:36:51.260 回答