WCF + TransactionScopeRequired + async + throw fault => ProtocolException "транзакция, в которой выполнялся метод, была асинхронно прервана" - PullRequest
0 голосов
/ 11 марта 2019

Вызов операции WCF, помеченной как «TransactionScopeRequired = true», для службы, которая использует асинхронную реализацию хоста (в моем случае использует Tasks) и генерирует исключение асинхронно (т.е. из продолжения), всегда приводит к следующему исключению(вместо исключения, которое я выбросил), получаемого на клиенте:

System.AggregateException: One or more errors occurred. ---> System.ServiceModel.ProtocolException: The transaction under which this method call was executing was asynchronously aborted.
   at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass6_0.<CreateTask>b__0(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
   --- End of inner exception stack trace ---
   at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
   at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
   at System.Threading.Tasks.Task.Wait()
   at WcfTransactionScopeRequiredAsync.Program.Main(String[] args) in D:\\WcfTransactionScopeRequiredAsync\\WcfTransactionScopeRequiredAsync.Client\\Program.cs:line 19
---> (Inner Exception #0) System.ServiceModel.ProtocolException: The transaction under which this method call was executing was asynchronously aborted.
   at System.ServiceModel.Channels.ServiceChannel.ThrowIfFaultUnderstood(Message reply, MessageFault fault, String action, MessageVersion version, FaultConverter faultConverter)
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass6_0.<CreateTask>b__0(IAsyncResult asyncResult)
   at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)<---

Сервисный контракт:

[ServiceContract]
public interface IService
{
    [FaultContract(typeof(SomeFault))]
    [TransactionFlow(TransactionFlowOption.Allowed)]
    [OperationContract]
    Task DoItAsync();
}

Реализация хоста сервиса:

[OperationBehavior(TransactionScopeRequired = true)]
public async Task DoItAsync()
{
    using (TransactionScope tx = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled))
    {
        await Task.Delay(1000).ConfigureAwait(continueOnCapturedContext: false);
        throw new FaultException<SomeFault>(new SomeFault());
        tx.Complete();
    }
}

Клиентявляется синхронным:

service.DoItAsync().Wait();

В этом случае клиент не выполняет транзакцию (поэтому это определенно не распределенная транзакция (проверяется проверкой Transaction.Current.TransactionInformation.DistributedIdentifier).

Этот код был протестирован с платформой .NET 4.7.2.

До сих пор мое исследование показало, что это ProtocolException генерируется конвейером WCF на хосте службы (также очевидно из трассировки стека в исключении) и предположительно будетбыть предназначенным для решения проблемы прерывания распределенных транзакцийсторона контроля хоста (т. е. когда DTC выдает прерывание).Хотя это, кажется, имеет некоторый смысл, это явно не тот случай.

Есть ли способ избежать этого ProtocolException, все еще используя асинхронную реализацию на хосте?

1 Ответ

0 голосов
/ 11 марта 2019

Есть ли способ избежать этого ProtocolException при использовании асинхронной реализации на хосте?

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

Я соответственно изменил приведенный выше код реализации узла службы, и исключение ProtocolException исчезло (теперь я получаю исключение FaultException на клиенте).

Комментарии к этому сообщению поставили меня на правильный путь.

...