C # WCF закрытие каналов и использование функций Func <T> - PullRequest
4 голосов
/ 18 июля 2011

В этом все дело, у меня есть служба WCF, она работает сейчас.Поэтому я начинаю работать на стороне клиента.И когда приложение работало, возникло исключение: тайм-аут.Итак, я начал читать, есть много примеров того, как сохранить соединение живым, но я также обнаружил, что лучший способ - создать канал, использовать его и распоряжаться им.И, честно говоря, мне это понравилось.Итак, теперь, читая о лучшем способе закрытия канала, есть две ссылки, которые могут быть полезны всем, кто в них нуждается:

1. Очистите клиентов правильным способом

2.Использование Func

В первой ссылке это пример:

    IIdentityService _identitySvc;
...
if (_identitySvc != null)
 {
     ((IClientChannel)_identitySvc).Close();
     ((IDisposable)_identitySvc).Dispose();
     _identitySvc = 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 ]

3 голосов
/ 18 июля 2011

метод UseService принимает делегата, который использует канал для отправки запроса. У делегата есть параметр и возвращаемое значение. Вы можете поместить вызов в службу WCF в делегате.

А в UseService он создает канал и передает его делегату, который вы должны предоставить. После завершения вызова он закрывает канал.

2 голосов
/ 18 июля 2011

Прокси-объект реализует больше, чем просто ваш контракт - он также реализует 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
{

}
1 голос
/ 31 декабря 2011

Чтобы ответить на вопрос, оставленный в комментарии в ответе Джейсона, простой пример GetCachedFactory может выглядеть следующим образом. В этом примере выполняется поиск конечной точки для создания путем нахождения конечной точки в файле конфигурации с атрибутом «Контракт», равным 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);
}
...