2013-02-15 13 views
7

Ho un MVC4 API REST che tenta di avviare un processo in una nuova discussione. Sto lavorando con il framework 4.5 e sto cercando di usare sync e attendere clausole.Avvio processo MVC4 WebApi

mio codice è simile:

[AcceptVerbs("POST")] 
public HttpResponseMessage Launch(string id) 
{ 
    runProcessAsync(id); // 1               

    return Request.CreateResponse(HttpStatusCode.Accepted); // 2 
} 

protected async void runProcessAsync(string id) 
{ 
    int exitcode = await runProcess(id); // 3 
    string exitcodesz = string.Format("exitcode: {0}", exitcode); // 4 
    // do more stuff with exitcode 
} 

protected Task<int> runProcess(string id) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    var process = new Process 
    { 
     StartInfo = { FileName = @"C:\a_very_slow_task.bat" }, 
      EnableRaisingEvents = true 
     }; 
     process.Exited += (sender, args) => tcs.SetResult(((Process)sender).ExitCode);    

     process.Start(); 

     return tcs.Task; 
    } 
} 

Idealmente, qualcuno si esibirà una chiamata API REST (/ compito/slowtask/lancio) con POST, verbo e si aspettano un 202 (accettati) molto veloce.

Utilizzo di Fiddler Web Debugger Effettuo la richiesta, il codice viene inserito in Launch (// 1), quindi passa all'attesa (// 3), l'attività lenta viene creata e avviata e viene restituito Accepted (// 2). Ma a questo punto, Fiddler non mostra il risultato 202. Vedere l'immagine allegata:

http://imageshack.us/photo/my-images/703/fiddler1.png/

Quando l'operazione lenta termina, il codice continua catturare l'ExitCode (// 4) e quindi il 202 viene catturato in Fiddler.

Questo è molto strano, perché ho fatto il ritorno molto tempo fa. Cosa mi manca? come posso cambiare quel codice per restituire un 202 molto velocemente e dimenticarmi del compito.

Nota: So come farlo con le funzionalità non framework 4.5, sto cercando di imparare come usare async/await.

+1

Penso che questo sia dovuto al fatto che 'runProcessAsync()' viene eseguito nel contesto di sincronizzazione della richiesta. Se non lo vuoi, puoi usare 'Task.Run (() => runProcessAsync (id));'. Ma fai attenzione, perché IIS può riciclare il tuo dominio in quel periodo, quindi c'è una possibilità che "runProcessAsync()" non venga completato. – svick

+0

L'ho fatto e funziona. È un peccato che tu non abbia risposto correttamente, perché hai risolto il mio problema. – Jordi

risposta

4

Penso che questo sia dovuto al fatto che runProcessAsync() viene eseguito nel contesto di sincronizzazione della richiesta. Se non lo desideri, puoi utilizzare Task.Run(() => runProcessAsync(id));. Ma fai attenzione, perché IIS può riciclare il tuo AppDomain durante quel periodo, quindi c'è la possibilità che runProcessAsync() non venga completato.

1

Il modo più semplice è cambiare semplicemente runProcessAsync per restituire un Task. Ricorda che i metodi async devono restituire Task/Task<T> quando possibile, e restituire solo void quando hanno a.

Tuttavia, devo avvertirvi che questo è piuttosto pericoloso. ASP.NET è un server HTTP, quindi se non ci sono richieste attive, si sentirà libero di rimuovere il tuo AppDomain se lo desidera. Ciò significherebbe che il tuo "fare più cose con exitcode" lascerà semplicemente la faccia della terra.

Ho un blog post with a BackgroundTaskManager che è possibile utilizzare per registrare Task s con il runtime ASP.NET. Tenterà di ritardare gli arresti di AppDomain se ci sono registrati Task s non completati. Tuttavia, è solo un "miglior sforzo"; non ci sono garanzie per quel genere di cose. Qualsiasi codice in esecuzione in ASP.NET che non è associato a una richiesta attiva è una situazione pericolosa.

+0

Non capisco, perché cambierei il tipo di ritorno dell'aiuto 'Task'? Se il problema riguarda il contesto di sincronizzazione, allora non cambierà nulla, no? – svick

Problemi correlati