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

时间:2011-07-18 02:39:59

标签: c# wcf func channelfactory

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

1. Clean up clients, the right way

2. Using Func

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

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

因此,如果通道不为null,则关闭,处理并指定null。但我有一个小问题。在这个例子中,通道有一个.Close()方法,但在我的例子中,intellisense没有显示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);

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

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

3 个答案:

答案 0 :(得分:3)

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

在UseService中,它创建频道并将频道传递给代表,该代表应由您提供。完成通话后,它会关闭频道。

答案 1 :(得分: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
{

}

答案 2 :(得分: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);
}