Как лучше всего обойти проблему с клиентом WCF, использующим блокировку?

Мне нравится создавать экземпляры моих клиентов службы WCF в блоке using, поскольку это в значительной степени стандартный способ использования ресурсов, которые реализуют IDisposable:

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

Но, как отмечено в этой статье MSDN , упаковка клиента WCF в блок using может маскировать любые ошибки, которые приводят к тому, что клиент остается в неисправном состоянии (например, время ожидания или проблема связи). Короче говоря, когда вызывается Dispose (), клиентский метод Close () запускается, но выдает ошибку, потому что он находится в неисправном состоянии. Исходное исключение затем маскируется вторым исключением. Нехорошо.

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

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

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

К счастью, я нашел несколько других обходных путей, таких как этот, на IServiceOriented. Вы начинаете с:

public delegate void UseServiceDelegate(T proxy); 

public static class Service 
{ 
    public static ChannelFactory _channelFactory = new ChannelFactory(""); 

    public static void Use(UseServiceDelegate codeBlock) 
    { 
        IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel(); 
        bool success = false; 
        try 
        { 
            codeBlock((T)proxy); 
            proxy.Close(); 
            success = true; 
        } 
        finally 
        { 
            if (!success) 
            { 
                proxy.Abort(); 
            } 
        } 
     } 
} 

, который затем позволяет:

Service.Use(orderService => 
{ 
    orderService.PlaceOrder(request); 
}); 

Это неплохо, но я не думаю, что это так выразительно и легко понятно, как блок using.

Обходной путь, который я сейчас пытаюсь использовать, о котором я впервые прочитал в блоге . davidbarret. нетто . В основном вы переопределяете метод клиента Dispose(), где бы вы его ни использовали. Что-то вроде:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

Похоже, что это позволяет снова разрешить блок using без опасности замаскировать исключение состояния ошибки.

Итак, есть ли другие ошибки, которые я должен обратить внимание на использование этих обходных путей? Кто-нибудь придумал что-нибудь лучше?

вопрос задан 21.02.2009
Eric King
7615 репутация

26 ответов


  • 127 рейтинг

    На самом деле, хотя я написал в блоге (см. ответ Люка ), я думаю, что этот лучше, чем моя IDisposable оболочка. Типичный код:

    Service.Use(orderService=>
    {
      orderService.PlaceOrder(request);
    }); 
    

    (изменить на комментарии)

    Так как Use возвращает void, самый простой способ обработки возвращаемых значений - через захваченную переменную:

    int newOrderId = 0; // need a value for definite assignment
    Service.Use(orderService=>
      {
        newOrderId = orderService.PlaceOrder(request);
      });
    Console.WriteLine(newOrderId); // should be updated
    
    ответ дан Marc Gravell, с репутацией 759786, 21.02.2009
  • 84 рейтинг

    Предоставлен выбор между решением, отстаиваемым IServiceOriented. com и решение, отстаиваемое блогом Дэвида Баррета , я предпочитаю простоту, предлагаемую путем переопределения клиентского метода Dispose (). Это позволяет мне продолжать использовать оператор using () так, как можно было бы ожидать с одноразовым объектом. Однако, как указал @Brian, это решение содержит условие состязания, состоящее в том, что состояние может не быть сбойным при проверке, а может быть к моменту вызова Close (), и в этом случае CommunicationException по-прежнему возникает.

    Итак, чтобы обойти это, я использовал решение, которое сочетает в себе лучшее из обоих миров.

    void IDisposable.Dispose()
    {
        bool success = false;
        try 
        {
            if (State != CommunicationState.Faulted) 
            {
                Close();
                success = true;
            }
        } 
        finally 
        {
            if (!success) 
                Abort();
        }
    }
    
    ответ дан Matt Davis, с репутацией 36589, 14.09.2009
  • 28 рейтинг

    Я написал функцию высшего порядка , чтобы она работала правильно. Мы использовали это в нескольких проектах, и это, кажется, работает отлично. Вот как все должно было быть сделано с самого начала, без парадигмы «использования» и так далее.

    TReturn UseService(Func code)
    {
        var chanFactory = GetCachedFactory();
        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);
    

    Это почти так же, как и в вашем примере. В некоторых проектах мы пишем строго типизированные вспомогательные методы, поэтому в итоге мы пишем что-то вроде "Wcf. UseFooService (F = & GT; ф. , , )».

    Я считаю это довольно элегантным, учитывая все обстоятельства. Есть ли конкретная проблема, с которой вы столкнулись?

    Это позволяет подключать другие интересные функции. Например, на одном сайте сайт аутентифицируется в службе от имени вошедшего в систему пользователя. (Сайт не имеет учетных данных сам по себе. ) Путем написания собственного помощника метода «UseService» мы можем настроить фабрику каналов так, как мы хотим, и т. Д. Мы также не обязаны использовать сгенерированные прокси - подойдет любой интерфейс.

    ответ дан MichaelGG, с репутацией 8722, 21.02.2009
  • 26 рейтинг

    Это рекомендуемый Microsoft способ обработки вызовов клиента WCF:

    Подробнее см .: Ожидаемые исключения

    try
    {
        ...
        double result = client.Add(value1, value2);
        ...
        client.Close();
    }
    catch (TimeoutException exception)
    {
        Console.WriteLine("Got {0}", exception.GetType());
        client.Abort();
    }
    catch (CommunicationException exception)
    {
        Console.WriteLine("Got {0}", exception.GetType());
        client.Abort();
    }
    

    Дополнительная информация Кажется, что так много людей задают этот вопрос на WCF, что Microsoft даже создала специальный образец, чтобы продемонстрировать, как обрабатывать исключения:

    c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client

    Скачать образец: C # или VB

    Учитывая, что существует проблем, связанных с оператором using , (с подогревом? ) Внутренние обсуждения и потоков по этому вопросу, я не собираюсь тратить свое время, пытаясь стать ковбоем кода и найти более чистый путь. Я просто смирюсь с этим и внедрю клиентов WCF таким подробным (но доверенным) способом для моих серверных приложений.

    Необязательные дополнительные сбои при перехвате

    Многие исключения происходят из CommunicationException, и я не думаю, что большинство из этих исключений следует повторить. Я пролистал каждое исключение в MSDN и нашел короткий список повторяющихся исключений (в дополнение к TimeOutException выше). Дайте мне знать, если я пропустил исключение, которое следует повторить.

      // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
    catch (ChannelTerminatedException cte)
    {
    secureSecretService.Abort();
    // todo: Implement delay (backoff) and retry
    }
    
    // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
    // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
    catch (EndpointNotFoundException enfe)
    {
    secureSecretService.Abort();
    // todo: Implement delay (backoff) and retry
    }
    
    // The following exception that is thrown when a server is too busy to accept a message.
    catch (ServerTooBusyException stbe)
    {
    secureSecretService.Abort();
    // todo: Implement delay (backoff) and retry
    }
    

    Правда, это немного обыденного кода для написания. В настоящее время я предпочитаю этот ответ и не вижу в этом коде никаких «взломов», которые могли бы вызвать проблемы в будущем.

    ответ дан random65537, с репутацией 21348, 19.02.2011
  • 14 рейтинг

    Наконец-то я нашел несколько серьезных шагов к чистому решению этой проблемы.

    Этот пользовательский инструмент расширяет WCFProxyGenerator для предоставления прокси обработки исключений. Он генерирует дополнительный прокси с именем ExceptionHandlingProxy, который наследует ExceptionHandlingProxyBase - последний из которых реализует основные функции прокси. В результате вы можете использовать прокси-сервер по умолчанию, который наследует ClientBase или ExceptionHandlingProxy, который управляет временем жизни фабрики каналов и канала. ExceptionHandlingProxy учитывает ваш выбор в диалоговом окне «Добавить ссылку на службу» в отношении асинхронных методов и типов коллекций.

    Codeplex имеет проект под названием Обработка исключений Генератор прокси WCF . Он в основном устанавливает новый пользовательский инструмент в Visual Studio 2008, а затем использует этот инструмент для создания нового прокси-сервера (Добавить ссылку на сервис) . Он имеет некоторые хорошие функции для работы с неисправными каналами, тайм-аутами и безопасным удалением. Здесь есть отличное видео под названием ExceptionHandlingProxyWrapper , объясняющее, как именно это работает.

    Можно снова безопасно использовать оператор Using, и, если канал неисправен по какому-либо запросу (TimeoutException или CommunicationException), оболочка повторно инициализирует неисправный канал и повторяет запрос. Если это не удается, он вызовет команду Abort(), утилизирует прокси и сбросит исключение. Если служба сгенерирует код FaultException, она прекратит выполнение, и прокси-сервер будет безопасно прерван, выдав правильное исключение, как и ожидалось.

    ответ дан Neil, с репутацией 1762, 22.01.2010
  • 10 рейтинг

    Основываясь на ответах Марка Гравелла, MichaelGG и Мэтта Дэвиса, наши разработчики придумали следующее:

    public static class UsingServiceClient
    {
        public static void Do(TClient client, Action execute)
            where TClient : class, ICommunicationObject
        {
            try
            {
                execute(client);
            }
            finally
            {
                client.DisposeSafely();
            }
        }
    
        public static void DisposeSafely(this ICommunicationObject client)
        {
            if (client == null)
            {
                return;
            }
    
            bool success = false;
    
            try
            {
                if (client.State != CommunicationState.Faulted)
                {
                    client.Close();
                    success = true;
                }
            }
            finally
            {
                if (!success)
                {
                    client.Abort();
                }
            }
        }
    }
    

    Пример использования:

    string result = string.Empty;
    
    UsingServiceClient.Do(
        new MyServiceClient(),
        client =>
        result = client.GetServiceResult(parameters));
    

    Это максимально приближенный к синтаксису "using", вам не нужно возвращать фиктивное значение при вызове метода void, и вы можете сделать несколько вызовов службы (и вернуть несколько значений) без использования кортежей.

    Кроме того, при желании вы можете использовать это с потомками ClientBase вместо ChannelFactory.

    Метод расширения предоставляется, если разработчик хочет вместо этого вручную удалить прокси / канал.

    ответ дан TrueWill, с репутацией 19262, 24.08.2012
  • 8 рейтинг

    @Marc Gravell

    Не было бы нормально использовать это:

    public static TResult Using(this T client, Func work)
            where T : ICommunicationObject
    {
        try
        {
            var result = work(client);
    
            client.Close();
    
            return result;
        }
        catch (Exception e)
        {
            client.Abort();
    
            throw;
        }
    }
    

    Или то же самое (Func) в случае Service.Use

    Это облегчит возврат переменных.

    ответ дан codeRecap, с репутацией 508, 2.05.2013
  • 7 рейтинг

    Что это?

    Это версия принятого ответа CW, но с включенной (что я считаю завершенной) обработкой исключений.

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

    Простое использование клиента WCF

    После того, как вы создадите свой прокси на стороне клиента, это все, что вам нужно для его реализации.

    Service.Use(orderService=>
    {
      orderService.PlaceOrder(request);
    });
    

    ServiceDelegate. CS

    Добавьте этот файл в ваше решение. Никаких изменений в этом файле не требуется, если только вы не хотите изменить количество повторных попыток или какие исключения вы хотите обработать.

    public delegate void UseServiceDelegate(T proxy);
    
    public static class Service
    {
        public static ChannelFactory _channelFactory = new ChannelFactory(""); 
    
        public static void Use(UseServiceDelegate codeBlock)
        {
            IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
            bool success = false;
    
    
           Exception mostRecentEx = null;
           int millsecondsToSleep = 1000;
    
           for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
           {
               try
               {
                   codeBlock((T)proxy);
                   proxy.Close();
                   success = true; 
                   break;
               }
    
               // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
               catch (ChannelTerminatedException cte)
               {
                  mostRecentEx = cte;
                   proxy.Abort();
                   //  delay (backoff) and retry 
                   Thread.Sleep(millsecondsToSleep  * (i + 1)); 
               }
    
               // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
               // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
               catch (EndpointNotFoundException enfe)
               {
                  mostRecentEx = enfe;
                   proxy.Abort();
                   //  delay (backoff) and retry 
                   Thread.Sleep(millsecondsToSleep * (i + 1)); 
               }
    
               // The following exception that is thrown when a server is too busy to accept a message.
               catch (ServerTooBusyException stbe)
               {
                  mostRecentEx = stbe;
                   proxy.Abort();
    
                   //  delay (backoff) and retry 
                   Thread.Sleep(millsecondsToSleep * (i + 1)); 
               }
               catch (TimeoutException timeoutEx)
               {
                   mostRecentEx = timeoutEx;
                   proxy.Abort();
    
                   //  delay (backoff) and retry 
                   Thread.Sleep(millsecondsToSleep * (i + 1)); 
               } 
               catch (CommunicationException comException)
               {
                   mostRecentEx = comException;
                   proxy.Abort();
    
                   //  delay (backoff) and retry 
                   Thread.Sleep(millsecondsToSleep * (i + 1)); 
               }
               catch(Exception )
               {
                    // rethrow any other exception not defined here
                    // You may want to define a custom Exception class to pass information such as failure count, and failure type
                    proxy.Abort();
                    throw ;  
               }
           }
           if (success == false && mostRecentEx != null) 
           { 
               proxy.Abort();
               throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
           }
    
        }
    }
    

    PS: я сделал это сообщение вики сообщества. Я не буду набирать «баллы» из этого ответа, но предпочитаю, чтобы вы проголосовали за него, если вы согласны с реализацией, или измените его, чтобы сделать его лучше.

    ответ дан random65537, с репутацией 21348, 21.02.2012
  • 7 рейтинг

    Ниже приведена расширенная версия источника от к вопросу и расширена для кэширования нескольких канальных фабрик и попытки поиска конечной точки в файле конфигурации по имени контракта.

    Он использует. NET 4 (в частности: контравариантность, LINQ, var):

    /// 
    /// Delegate type of the service method to perform. ///
     
    /// 
    The service proxy.
    /// The type of service to use.
    internal delegate void UseServiceDelegate(T proxy);
    
    /// 
    /// Wraps using a WCF service. ///
     
    /// The type of service to use.
    internal static class Service
    {
        /// 
    /// A dictionary to hold looked-up endpoint names. ///
     
        private static readonly IDictionary cachedEndpointNames = new Dictionary();
    
        /// 
    /// A dictionary to hold created channel factories. ///
     
        private static readonly IDictionary> cachedFactories =
            new Dictionary>();
    
        /// 
    /// Uses the specified code block. ///
     
        /// 
    The code block.
        internal static void Use(UseServiceDelegate codeBlock)
        {
            var factory = GetChannelFactory();
            var proxy = (IClientChannel)factory.CreateChannel();
            var success = false;
    
            try
            {
                using (proxy)
                {
                    codeBlock((T)proxy);
                }
    
                success = true;
            }
            finally
            {
                if (!success)
                {
                    proxy.Abort();
                }
            }
        }
    
        /// 
    /// Gets the channel factory. ///
     
        /// The channel factory.
        private static ChannelFactory GetChannelFactory()
        {
            lock (cachedFactories)
            {
                var endpointName = GetEndpointName();
    
                if (cachedFactories.ContainsKey(endpointName))
                {
                    return cachedFactories[endpointName];
                }
    
                var factory = new ChannelFactory(endpointName);
    
                cachedFactories.Add(endpointName, factory);
                return factory;
            }
        }
    
        /// 
    /// Gets the name of the endpoint. ///
     
        /// The name of the endpoint.
        private static string GetEndpointName()
        {
            var type = typeof(T);
            var fullName = type.FullName;
    
            lock (cachedFactories)
            {
                if (cachedEndpointNames.ContainsKey(type))
                {
                    return cachedEndpointNames[type];
                }
    
                var serviceModel = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).SectionGroups["system.serviceModel"] as ServiceModelSectionGroup;
    
                if ((serviceModel != null) && !string.IsNullOrEmpty(fullName))
                {
                    foreach (var endpointName in serviceModel.Client.Endpoints.Cast().Where(endpoint => fullName.EndsWith(endpoint.Contract)).Select(endpoint => endpoint.Name))
                    {
                        cachedEndpointNames.Add(type, endpointName);
                        return endpointName;
                    }
                }
            }
    
            throw new InvalidOperationException("Could not find endpoint element for type '" + fullName + "' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.");
        }
    }
    
    ответ дан Jesse C. Slicer, с репутацией 16879, 30.08.2011
  • 5 рейтинг

    Оболочка, как это будет работать:

    public class ServiceClientWrapper : IDisposable
    {
        private ServiceType _channel;
        public ServiceType Channel
        {
            get { return _channel; }
        }
    
        private static ChannelFactory _channelFactory;
    
        public ServiceClientWrapper()
        {
            if(_channelFactory == null)
                 // Given that the endpoint name is the same as FullName of contract.
                _channelFactory = new ChannelFactory(typeof(T).FullName);
            _channel = _channelFactory.CreateChannel();
            ((IChannel)_channel).Open();
        }
    
        public void Dispose()
        {
            try
            {
                ((IChannel)_channel).Close();
            }
            catch (Exception e)
            {
                ((IChannel)_channel).Abort();
                // TODO: Insert logging
            }
        }
    }
    

    Это должно позволить вам написать код как:

    ResponseType response = null;
    using(var clientWrapper = new ServiceClientWrapper())
    {
        var request = ...
        response = clientWrapper.Channel.MyServiceCall(request);
    }
    // Use your response object.
    

    Оболочка, конечно, может перехватывать больше исключений, если это требуется, но принцип остается тем же.

    ответ дан Tomas Jansson, с репутацией 14703, 7.12.2010
  • 4 рейтинг

    Если вам не нужен IoC или вы используете автоматически сгенерированный клиент (Service Reference), то вы можете просто использовать оболочку для управления закрытием и позволить GC принять клиентскую базу, когда она находится в безопасном состоянии, которое будет Не выбрасывайте никаких исключений. GC вызовет Dispose in serviceclient, и это вызовет Close. Поскольку он уже закрыт, он не может причинить никакого ущерба. Я использую это без проблем в производственном коде.

    public class AutoCloseWcf : IDisposable
    {
    
        private ICommunicationObject CommunicationObject;
    
        public AutoDisconnect(ICommunicationObject CommunicationObject)
        {
            this.CommunicationObject = CommunicationObject;
        }
    
        public void Dispose()
        {
            if (CommunicationObject == null)
                return;
            try {
                if (CommunicationObject.State != CommunicationState.Faulted) {
                    CommunicationObject.Close();
                } else {
                    CommunicationObject.Abort();
                }
            } catch (CommunicationException ce) {
                CommunicationObject.Abort();
            } catch (TimeoutException toe) {
                CommunicationObject.Abort();
            } catch (Exception e) {
                CommunicationObject.Abort();
                //Perhaps log this
    
            } finally {
                CommunicationObject = null;
            }
        }
    }
    

    Затем, когда вы обращаетесь к серверу, вы создаете клиент и используете using в autodisconect:

    var Ws = new ServiceClient("netTcpEndPointName");
    using (new AutoCloseWcf(Ws)) {
    
        Ws.Open();
    
        Ws.Test();
    }
    
    ответ дан Luiz Felipe, с репутацией 41, 7.09.2011
  • 4 рейтинг

    Я использовал динамический прокси Castle для решения проблемы Dispose (), а также реализовал автоматическое обновление канала, когда он находится в нерабочем состоянии. Чтобы использовать это, вы должны создать новый интерфейс, который наследует ваш контракт на обслуживание и IDisposable. Динамический прокси реализует этот интерфейс и оборачивает канал WCF:

    Func createChannel = () =>
        ChannelFactory .CreateChannel(new NetTcpBinding(), new EndpointAddress(uri)); var factory = new WcfProxyFactory(); var proxy = factory.Create(createChannel); proxy.HelloWorld(); 

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

    Посмотрите на код, на самом деле он довольно прост: WCF Dynamic Proxy

    ответ дан Jay Douglass, с репутацией 4504, 6.01.2012
  • 1 рейтинг
    public static class Service
    {
        public static ChannelFactory ChannelFactory = new ChannelFactory("*");
    
        public static TReturn Use(Func codeBlock)
        {
            var proxy = (IClientChannel)ChannelFactory.CreateChannel();
            var success = false;
            try
            {
                var result = codeBlock((TChannel)proxy);
                proxy.Close();
                success = true;
                return result;
            }
            finally
            {
                if (!success)
                {
                    proxy.Abort();
                }
            }
        }
    }
    

    Так что это позволяет красиво написать операторы возврата:

    return Service.Use(orderService => 
    { 
        return orderService.PlaceOrder(request); 
    }); 
    
    ответ дан Andriy Buday, с репутацией 1579, 8.05.2013
  • 1 рейтинг

    В нашей системной архитектуре часто используется инфраструктура IoC 385268139 Unity ​​для создания экземпляров ClientBase, поэтому не существует надежного способа обеспечить использование другими разработчиками блоков using{}. Чтобы сделать его как можно более надежным, я создал этот пользовательский класс, который расширяет ClientBase и обрабатывает закрытие канала при утилизации или при финализации в случае, если кто-то явно не утилизирует созданный Unity экземпляр.

    Есть также вещи, которые нужно было сделать в конструкторе, чтобы настроить канал для пользовательских учетных данных и прочее, так что это тоже здесь. , ,

    public abstract class PFServer2ServerClientBase : ClientBase, IDisposable where TChannel : class
    {
        private bool disposed = false;
    
        public PFServer2ServerClientBase()
        {
            // Copy information from custom identity into credentials, and other channel setup...
        }
    
        ~PFServer2ServerClientBase()
        {
            this.Dispose(false);
        }
    
        void IDisposable.Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        public void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                try
                {
                        if (this.State == CommunicationState.Opened)
                            this.Close();
                }
                finally
                {
                    if (this.State == CommunicationState.Faulted)
                        this.Abort();
                }
                this.disposed = true;
            }
        }
    }
    

    Тогда клиент может просто:

    internal class TestClient : PFServer2ServerClientBase, ITest
    {
        public string TestMethod(int value)
        {
            return base.Channel.TestMethod(value);
        }
    }
    

    И абонент может сделать любое из следующих:

    public SomeClass
    {
        [Dependency]
        public ITest test { get; set; }
    
        // Not the best, but should still work due to finalizer.
        public string Method1(int value)
        {
            return this.test.TestMethod(value);
        }
    
        // The good way to do it
        public string Method2(int value)
        {
            using(ITest t = unityContainer.Resolve())
            {
                return t.TestMethod(value);
            }
        }
    }
    

    ответ дан CodingWithSpike, с репутацией 32183, 24.02.2009
  • 1 рейтинг

    Я написал простой базовый класс , который обрабатывает это. Он доступен в виде пакета NuGet и довольно прост в использовании.

    //MemberServiceClient is the class generated by SvcUtil
    public class MemberServiceManager : ServiceClientBase
    {
        public User GetUser(int userId)
        {
            return PerformServiceOperation(client => client.GetUser(userId));
        }
    
        //you can also check if any error occured if you can't throw exceptions       
        public bool TryGetUser(int userId, out User user)
        {
            return TryPerformServiceOperation(c => c.GetUser(userId), out user);
        }
    }
    
    ответ дан Ufuk Hacıoğulları, с репутацией 29696, 15.01.2013