Мне нравится создавать экземпляры моих клиентов службы 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>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
Который затем позволяет:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Это не плохо, но я не думаю, что это так выразительно и легко понятно, как using
блок.
Обходной путь, который я в настоящее время пытаюсь использовать, я сначала прочитал на blog.davidbarret.net . В основном вы переопределяете метод клиента Dispose()
везде, где вы его используете. Что-то вроде:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
По-видимому, это позволяет using
снова разрешить блокировку без опасности замаскировать исключение неисправного состояния.
Итак, есть ли другие ошибки, которые я должен посмотреть, чтобы использовать эти обходные пути? Кто-нибудь придумал что-нибудь лучше?
Action<T>
вместоUseServiceDelegate<T>
. незначительный.Service<T>
поскольку он усложняет юнит-тестирование (как это делают большинство статических вещей). Я бы предпочел, чтобы он был нестатичным, чтобы его можно было внедрить в класс, который его использует.Ответы:
На самом деле, хотя я веду блог (см . Ответ Люка ), я думаю, что это лучше, чем моя IDisposable оболочка. Типичный код:
(редактировать по комментариям)
Так как
Use
возвращает void, самый простой способ обработки возвращаемых значений - через захваченную переменную:источник
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
иhttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
иhttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Учитывая выбор между решением, поддерживаемым IServiceOriented.com, и решением, поддерживаемым блогом Дэвида Баррета , я предпочитаю простоту, предложенную путем переопределения метода Dispose () клиента. Это позволяет мне продолжать использовать оператор using (), как можно ожидать с одноразовым объектом. Однако, как указал @Brian, это решение содержит условие состязания, состоящее в том, что состояние может не быть сбойным при проверке, а может быть к моменту вызова Close (), и в этом случае исключение CommunicationException по-прежнему возникает.
Итак, чтобы обойти это, я использовал решение, которое сочетает в себе лучшее из обоих миров.
источник
success
флаг? Почему нетtry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Я бы не хотел, чтобы было выброшено исключение, если бы я мог успешно прервать его в блоке finally. Я бы хотел, чтобы исключение было выдано только в случае сбоя Abort () в этом случае. Таким образом, try / catch скрыл бы потенциальное исключение состояния гонки и все же позволил бы вам прервать () соединение в блоке finally.Я написал функцию более высокого порядка, чтобы она работала правильно. Мы использовали это в нескольких проектах, и это, кажется, работает отлично. Вот как все должно было быть сделано с самого начала, без парадигмы «использования» и так далее.
Вы можете звонить так:
Это почти так же, как у вас в вашем примере. В некоторых проектах мы пишем строго типизированные вспомогательные методы, поэтому в итоге мы пишем такие вещи, как «Wcf.UseFooService (f => f ...)».
Я считаю это довольно элегантным, учитывая все обстоятельства. Есть ли конкретная проблема, с которой вы столкнулись?
Это позволяет подключать другие полезные функции. Например, на одном сайте сайт аутентифицируется в службе от имени вошедшего в систему пользователя. (Сайт сам по себе не имеет учетных данных.) Написав собственный помощник метода «UseService», мы можем настроить фабрику каналов так, как нам хочется, и т. Д. Мы также не обязаны использовать сгенерированные прокси - любой интерфейс подойдет ,
источник
GetCachedFactory
метод?Это рекомендуемый Microsoft способ обработки вызовов клиента WCF:
Для более подробной информации смотрите: Ожидаемые исключения
Дополнительная информация Так много людей задают этот вопрос на WCF, что Microsoft даже создала специальный образец, чтобы продемонстрировать, как обрабатывать исключения:
C: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ клиент
Скачать образец: C # или VB
Учитывая , что есть очень много вопросов , связанных с использованием заявления , ( с подогревом?) Внутреннее обсуждение и темы по этому вопросу, я не собираюсь тратить свое время , пытаясь стать Ковбой и найти более рациональный способ. Я просто смирюсь с этим и внедряю клиентов WCF таким подробным (но доверенным) способом для моих серверных приложений.
Необязательные дополнительные ошибки, чтобы поймать
Многие исключения происходят из,
CommunicationException
и я не думаю, что большинство из этих исключений следует повторить. Я пролистал каждое исключение в MSDN и нашел короткий список повторяющихся исключений (в дополнение кTimeOutException
описанному выше). Дайте мне знать, если я пропустил исключение, которое следует повторить.По общему признанию, это - немного мирского кода, чтобы написать. В настоящее время я предпочитаю этот ответ , и не вижу в этом коде «хаков», которые могут вызвать проблемы в будущем.
источник
"Hope this code wasn't important, because it might not happen."
по-прежнему выполняется ...Наконец-то я нашел несколько серьезных шагов к чистому решению этой проблемы.
У Codeplex есть проект под названием Генератор прокси WCF . Он в основном устанавливает новый пользовательский инструмент в Visual Studio 2008, а затем использует этот инструмент для создания нового прокси службы (Добавить ссылку на службу) . Он имеет некоторые хорошие функции для работы с неисправными каналами, тайм-аутами и безопасным удалением. Здесь есть отличное видео под названием ExceptionHandlingProxyWrapper, объясняющее, как именно это работает.
Вы можете безопасно использовать
Using
оператор снова, и если канал неисправен по какому-либо запросу (TimeoutException или CommunicationException), Оболочка повторно инициализирует неисправный канал и повторяет запрос. Если это не удастся, он вызоветAbort()
команду, утилизирует прокси и сбросит исключение. Если служба сгенерируетFaultException
код, она прекратит выполнение, и прокси-сервер будет благополучно прерван, выдав правильное исключение, как и ожидалось.источник
Основываясь на ответах Марка Гравелла, MichaelGG и Мэтта Дэвиса, наши разработчики придумали следующее:
Пример использования:
Он максимально приближен к синтаксису «using», вам не нужно возвращать фиктивное значение при вызове метода void, и вы можете сделать несколько вызовов службы (и вернуть несколько значений), не используя кортежи.
Кроме того, вы можете использовать это с
ClientBase<T>
потомками вместо ChannelFactory при желании.Метод расширения предоставляется, если разработчик хочет вместо этого вручную удалить прокси / канал.
источник
DisposeSafely
приватным, безусловно, вариант, и это поможет избежать путаницы. Могут быть случаи использования, когда кто-то захочет позвонить напрямую, но я не могу придумать один случайный прием.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
Не было бы нормально использовать это:
Или то же самое
(Func<T, TResult>)
в случаеService<IOrderService>.Use
Это сделает возврат переменных проще.
источник
Что это?
Это CW-версия принятого ответа, но с (что я считаю завершенным) обработкой исключений.
Принятый ответ ссылается на этот сайт, которого больше нет . Чтобы избавить вас от неприятностей, я включил самые важные части здесь. Кроме того, я немного изменил его, добавив обработку повторных исключений для обработки этих надоедливых сетевых таймаутов.
Простое использование клиента WCF
Как только вы создадите свой прокси на стороне клиента, это все, что вам нужно для его реализации.
ServiceDelegate.cs
Добавьте этот файл в ваше решение. В этом файле не требуется никаких изменений, если только вы не хотите изменить количество повторных попыток или какие исключения вы хотите обработать.
PS: я сделал это сообщение вики сообщества. Я не буду набирать «баллы» из этого ответа, но предпочитаю, чтобы вы проголосовали за него, если вы согласны с реализацией, или измените его, чтобы сделать его лучше.
источник
success == false
в окончательный оператор ifНиже приведена расширенная версия источника из вопроса, расширенная для кэширования нескольких канальных фабрик и попытки поиска конечной точки в файле конфигурации по имени контракта.
Он использует .NET 4 (в частности: contravariance, LINQ,
var
):источник
UseServiceDelegate<T>
вместоAction<T>
?Action<T>
работает так же хорошо.Обертка, как это будет работать:
Это должно позволить вам написать код вроде:
Оболочка, конечно, может поймать больше исключений, если это требуется, но принцип остается тем же.
источник
Dispose
IChannel, он может выдать исключение, если канал находится в неисправном состоянии, это проблема, так как Microsoft указывает, чтоDispose
никогда не следует выдавать. Так что код, приведенный выше, обрабатывает случай, когдаClose
выдается исключение. ЕслиAbort
бросает, это может быть что-то серьезно не так. Я написал сообщение в блоге об этом в декабре прошлого года: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperЯ использовал динамический прокси Castle для решения проблемы Dispose (), а также реализовал автоматическое обновление канала, когда он находится в нерабочем состоянии. Чтобы использовать это, вы должны создать новый интерфейс, который наследует ваш контракт на обслуживание и IDisposable. Динамический прокси реализует этот интерфейс и обертывает канал WCF:
Мне это нравится, поскольку вы можете использовать службы WCF без необходимости беспокоиться о деталях WCF. И нет никакой дополнительной ошибки, как другие решения.
Посмотрите на код, на самом деле он довольно прост: динамический прокси WCF
источник
Используйте метод расширения:
источник
Если вам не нужен IoC или вы используете автоматически сгенерированный клиент (Service Reference), тогда вы можете просто использовать оболочку для управления закрытием и позволить GC принять клиентскую базу, когда она находится в безопасном состоянии, которое не вызовет никаких исключений. GC вызовет Dispose в serviceclient, и это вызовет
Close
. Поскольку он уже закрыт, он не может причинить вреда. Я использую это без проблем в производственном коде.Затем, когда вы обращаетесь к серверу, вы создаете клиент и используете
using
в autodisconect:источник
Резюме
Используя методы, описанные в этом ответе, можно использовать службу WCF в блоке using со следующим синтаксисом:
Конечно, вы можете адаптировать это еще больше, чтобы получить более краткую модель программирования, специфичную для вашей ситуации - но дело в том, что мы можем создать реализацию
IMyService
повторного канала, которая правильно реализует одноразовый шаблон.подробности
Все ответы, приведенные до настоящего времени, решают проблему обхода «ошибки» в реализации канала WCF
IDisposable
. Ответ , который , кажется, предлагает наиболее лаконичная модель программирования ( что позволяет использоватьusing
блок распоряжаться неуправляемыми ресурсы) является это один - где прокси доработан для реализацииIDisposable
с черепашкой свободной реализацией. Проблема с этим подходом заключается в удобстве обслуживания - мы должны повторно реализовать эту функциональность для каждого используемого нами прокси. В варианте этого ответа мы увидим, как мы можем использовать композицию, а не наследование, чтобы сделать эту технику общей.Первая попытка
Существуют различные реализации для
IDisposable
реализации, но в качестве аргумента мы будем использовать адаптацию, использованную в настоящее время принятым ответом .Вооружившись вышеуказанными классами, мы можем теперь написать
Это позволяет нам использовать наш сервис, используя
using
блок:Создание этого общего
Все, что мы сделали до сих пор, это переформулировали решение Томаса . Что препятствует тому, чтобы этот код был универсальным, является фактом, что
ProxyWrapper
класс должен быть повторно реализован для каждого контракта на обслуживание, который мы хотим. Теперь мы рассмотрим класс, который позволяет нам динамически создавать этот тип с использованием IL:С нашим новым вспомогательным классом мы можем теперь написать
Обратите внимание, что вы также можете использовать ту же технику (с небольшими изменениями) для автоматически сгенерированных клиентов, наследующих
ClientBase<>
(вместо использованияChannelFactory<>
), или если вы хотите использовать другую реализациюIDisposable
для закрытия вашего канала.источник
Мне нравится этот способ закрытия соединения:
источник
Я написал простой базовый класс, который обрабатывает это. Он доступен в виде пакета NuGet и довольно прост в использовании.
источник
Так что это позволяет красиво написать операторы возврата:
источник
Я хотел бы добавить реализацию Service из ответа Марка Гравелла для случая использования ServiceClient вместо ChannelFactory.
источник
Для тех, кто заинтересован, вот перевод VB.NET принятого ответа (ниже). Я немного уточнил это для краткости, объединив некоторые советы других в этой теме.
Я признаю, что это не по теме для исходных тегов (C #), но так как я не смог найти версию этого прекрасного решения для VB.NET, я предполагаю, что другие тоже будут искать. Лямбда-перевод может быть немного сложным, поэтому я хотел бы избавить кого-то от неприятностей.
Обратите внимание, что эта конкретная реализация предоставляет возможность настройки
ServiceEndpoint
во время выполнения.Код:
Применение:
источник
В нашей системной архитектуре часто используется инфраструктура Unity IoC для создания экземпляров ClientBase, поэтому нет точного способа обеспечить использование другими разработчиками
using{}
блоков. Чтобы сделать его как можно более надежным, я создал этот пользовательский класс, который расширяет ClientBase и обрабатывает закрытие канала при утилизации или при финализации в случае, если кто-то явно не избавится от созданного Unity экземпляра.Есть также вещи, которые нужно было сделать в конструкторе, чтобы настроить канал для пользовательских учетных данных и прочее, так что это тоже здесь ...
Тогда клиент может просто:
И вызывающая сторона может сделать любой из этих:
источник
Я привел несколько ответов на этот пост и настроил его в соответствии с моими потребностями.
Я хотел иметь возможность что-то сделать с клиентом WCF, прежде чем использовать этот
DoSomethingWithClient()
метод.Вот вспомогательный класс:
И я могу использовать это как:
источник
У меня есть собственная оболочка для канала, которая реализует Dispose следующим образом:
Это, кажется, работает хорошо и позволяет использовать блок использования.
источник
Следующий помощник позволяет вызывать
void
и не пустые методы. Применение:Сам класс это:
источник
Переопределите клиентский Dispose () без необходимости создания прокси-класса на основе ClientBase, а также без необходимости управлять созданием канала и кэшированием ! (Обратите внимание, что WcfClient не является классом ABSTRACT и основан на ClientBase)
источник
Мой метод - создать унаследованный класс, который явно реализует IDisposable. Это полезно для людей, которые используют графический интерфейс для добавления ссылки на сервис (Add Service Reference). Я просто добавляю этот класс в проект, создавая ссылку на службу, и использую ее вместо клиента по умолчанию:
Примечание: это просто простая реализация dispose, вы можете реализовать более сложную логику dispose, если хотите.
Затем вы можете заменить все ваши звонки, сделанные обычным сервисным клиентом, на безопасные клиенты, например:
Мне нравится это решение, так как оно не требует, чтобы у меня был доступ к определениям интерфейса, и я могу использовать
using
оператор так, как ожидал, позволяя моему коду выглядеть более или менее одинаково.Вам все еще нужно будет обработать исключения, которые могут быть выброшены, как указано в других комментариях в этой теме.
источник
Вы также можете использовать
DynamicProxy
для расширенияDispose()
метода. Таким образом, вы можете сделать что-то вроде:источник