2015-03-26 3 views
7

Ho un codice che funziona esattamente come desiderato. Tuttavia, il nostro server di build aziendale rifiuta qualsiasi check-in che abbia un avviso del compilatore.Come convertire l'azione in Func <Task> senza eseguire l'attività?

Il seguente avviso è (come previsto) visualizzato per il costruttore Action con le conversioni di Action to Func, poiché non sto utilizzando un'attesa.

Questo metodo asincrono manca degli operatori di 'attesa' e verrà eseguito in modo sincrono. Prendi in considerazione l'utilizzo dell'operatore 'attendi' per attendere chiamate API non bloccanti o 'attendere Task.Run (...)' per eseguire il lavoro con CPU su un thread in background.

public class TransactionOperation 
{ 
    private readonly Func<Task> _operation; 
    private readonly Func<Task> _rollback; 

    public OperationStatus Status { get; private set; } 

    public TransactionOperation(Func<Task> operation, Func<Task> rollback) 
    { 
     _operation = operation; 
     _rollback = rollback; 
     Status = OperationStatus.NotStarted; 
    } 

    public TransactionOperation(Action operation, Action rollback) 
    { 
     _operation = async() => operation.Invoke(); 
     _rollback = async() => rollback.Invoke(); 
     Status = OperationStatus.NotStarted; 
    } 

    public async Task Invoke() 
    { 
     try 
     { 
      Status = OperationStatus.InProcess; 
      await _operation.Invoke(); 
      Status = OperationStatus.Completed; 
     } 
     catch (Exception ex) 
     { 
      //... 
     } 
    } 
} 

cosa è un modo corretto per riscrivere il codice in modo che l'azione sia correttamente convertito Funz senza essere eseguita ancora o creare un nuovo thread (cioè attendere Task.Run())?


Update - Risposta suggerita # 1

_operation =() => Nuova operazione (operation.Invoke);

_rollback =() => nuova attività (rollback.Invoke);

Ho provato prima. Fa sì che questo test unitario non ritorni mai.

[TestMethod, TestCategory("Unit Test")] 
public async Task Transaction_MultiStepTransactionExceptionOnFourthAction_CorrectActionsRolledBack() 
{ 
    var operation = new TransactionOperation(PerformAction,() => RollbackOperation(1)); 
    var operation2 = new TransactionOperation(PerformAction,() => RollbackOperation(2)); 
    var operation3 = new TransactionOperation(PerformAction,() => RollbackOperation(3)); 
    var operation4 = new TransactionOperation(ExceptionAction,() => RollbackOperation(4)); 
    var operation5 = new TransactionOperation(PerformAction,() => RollbackOperation(5)); 
    var transaction = new Transaction(new[] { operation, operation2, operation3, operation4, operation5 }); 

    await IgnoreExceptions(transaction.ExecuteAsync); 

    AssertActionsPerformedThrough(4); 
    AssertActionsRolledBackThrough(4); 
} 

Update - risposta accettata

private async Task ConvertToTask(Action action) 
{ 
    action.Invoke(); 
    await Task.FromResult(true); 
} 

Ecco il costruttore d'azione aggiornato:

public TransactionOperation(Action operation, Action rollback) 
{ 
    _operation =() => ConvertToTask(operation); 
    _rollback =() => ConvertToTask(rollback); 
    Status = OperationStatus.NotStarted; 
} 
+1

Perché sei fare async su una cosa che verrà eseguita in modo sincrono? –

+1

Bene, 'async() => {operation.Invoke(); attendere Task.FromResult (0); } 'Dovrebbe farlo, ma il commento sopra è ancora pertinente: perché vuoi racchiudere il codice sincrono come asincrono, in primo luogo? – Noseratio

risposta

9

Si può semplicemente utilizzare Task.FromResult(0):

_operation = async() => { operation.Invoke(); await Task.FromResult(0); }; 
+0

Qualsiasi motivo per non utilizzare 'attende Task.CompletedTask' piuttosto che' Task.FromResult (0) '? –

+1

@ShaulBehr: No. Semplicemente non esisteva al momento in cui è stata scritta la risposta. –

2

Vuoi dire come questo:

_operation =() => new Task(operation.Invoke); 
_rollback =() => new Task(rollback.Invoke); 

Le funzioni restituiranno quindi un'attività con l'azione desiderata che non è stata ancora avviata.

penso che sarà necessario aggiornare il metodo di Invoke al di sotto per ottenere il comportamento che stai cercando:

Status = OperationStatus.InProcess; 
await Task.Run(() => 
    { 
     _operation().Start(); 
     _operation().Wait(); 
    }); 
Status = OperationStatus.Completed; 
+0

Con la mia sintassi di chiamata corrente, ciò causa l'interruzione di un Test unità. Modifica la mia domanda/ –

+0

Cosa sta facendo il test unitario? – RagtimeWilly

+0

Ho visto i tuoi aggiornamenti. Sembra che chiamare "attende _operation.Invoke()" e chiamando "_operation.Invoke(). Start()" si escluda a vicenda. L'obiettivo è essere in grado di utilizzare la stessa sintassi di esecuzione. –