Вызов основанного на задачах асинхронного одностороннего метода обратного вызова из службы WCF
У меня есть служба WCF, которая использует контракт обратного вызова для уведомления клиентов, аналогично этому
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void NotifySomething();
}
и сервисный код, называющий это похоже на это
void NotifySomething()
{
try
{
this.callback.NotifySomething();
}
catch (Exception ex)
{
// Log the exception and eat it
}
}
Обратите внимание, что по конструкции уведомление о обратном вызове является необязательным, то есть приятно иметь, но не обязательно. Вот почему он помечен как OneWay
и реализация ест исключения.
Из-за некоторого недопонимания мы думали, что этого будет достаточно, чтобы использовать метод неблокирующего огня и забывания. Но, конечно, это не было правдой, поэтому при некоторых обстоятельствах он на некоторое время блокируется, что вызывает проблемы, потому что он вызывается из блока, синхронизированного с потоком. Поэтому мы решили сделать его асинхронным, изменив определение следующим образом
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
Task NotifySomething();
}
У меня нет проблем с реализацией клиента, мой вопрос, как вызвать его из сервиса. Вот что я думаю сделать
async void NotifySomething()
{
try
{
await this.callback.NotifySomething();
}
catch (AggregateException ex)
{
// Unwrap, log the exception(s) and eat it
}
catch (Exception ex)
{
// Log the exception and eat it
}
}
Теперь, так как все говорят async void
это не хорошая практика, это нормально использовать здесь? Какие еще варианты у меня есть? Каков рекомендуемый способ выполнить это в контексте службы WCF?
1 ответ
То, как вы написали, довольно безопасно, так как обрабатывает исключения. Вы также можете написать повторно используемый метод расширения, чтобы вам не пришлось его повторять.
Возможно, что-то вроде этого:
public static class Extensions
{
public static void FireAndForget(this Task task)
{
task.ContinueWith(t =>
{
// log exceptions
t.Exception.Handle((ex) =>
{
Console.WriteLine(ex.Message);
return true;
});
}, TaskContinuationOptions.OnlyOnFaulted);
}
}
public async Task FailingOperation()
{
await Task.Delay(2000);
throw new Exception("Error");
}
void Main()
{
FailingOperation().FireAndForget();
Console.ReadLine();
}