2015-05-27 13 views
6

Attualmente sto creando un'applicazione ASP.NET MVC. Sto cercando di aggiungere elementi alla proprietà ViewBag per tutte le pagine del mio sito. Per raggiungere questo obiettivo, ho creato un controller di base ereditato da tutti i controller del sito.Esegui metodo asincrono nel vuoto sottoposto a override

da aggiungere alle ViewBag, ho sovrascritto il metodo OnActionExecuting: protected override void OnActionExecuting(ActionExecutingContext filterContext)

So che i metodi OnActionExecuting per MVC5 non sono asincrone, ed è per questo che sto funzionando in questo problema. Ho bisogno di essere in grado di chiamare qualche forma del seguente codice per recuperare gli elementi nuovi e metterli nel ViewBag:

IList<PlaceListItemViewModel> places = await GetLatestPlaces(3); 
ViewBag.FooterLatestPlaces = places; 

Utilizzando la GetLatestPlaces(3).Result solo causa un deadlock, quindi questo non è un'opzione.

Qualcuno potrebbe darmi un'opzione su come raggiungere questo obiettivo. Inoltre, non è necessario ignorare il metodo OnActionExecuting, se esiste un altro modo per aggiungere elementi a ViewBag per ogni pagina (oltre a chiamare il codice da ogni singola azione in ogni controller), sono aperto a utilizzare tale metodo .

Purtroppo il metodo GetLatestPlaces non può essere asincrono, poiché utilizza il driver MongoDB 2.0, che è asincrono fino in fondo.

+0

Considerare la riprogettazione in modo da poter chiamare il metodo in un secondo momento ... Non esiste un modo * buono * per chiamare il metodo asincrono in modo sincrono. –

+0

Cosa intendi chiamare il metodo più tardi? Esiste un altro metodo per aggiungere dati al ViewBag (o simili) che possono essere utilizzati all'interno dei file di visualizzazione del rasoio senza aggiungere un mucchio di codice C# alle viste? – Juzzbott

risposta

10

Ci sono due metodi generali per ottenere ciò che si voleva:

  1. Uso ConfigureAwait(false) nella libreria async. Nel tuo caso dovresti farlo all'interno del metodo GetLatestPlaces().
  2. Chiamare il metodo async in un thread diverso per non bloccare il contesto corrente. Così il codice sarà simile a questa:

    IList<PlaceListItemViewModel> places = Task.Run(async() => await GetLatestPlaces(3)).Result; 
    

Per maggiori informazioni visita Stephen Cleary's excellent blog post.

+0

Il secondo metodo che hai descritto sta funzionando alla grande! Sarò sicuro di avere letto anche quel post sul blog. Saluti. – Juzzbott

+0

@Juzzbott Anche il primo metodo dovrebbe funzionare, leggi il post sul blog di Stephen e otterrai tutto il concetto. –

+2

Chiamare 'Task.Run' in un'applicazione ASP.NET danneggerà le prestazioni a causa dell'interruttore di contesto non necessario. –

0

Il motivo del deadlock viene catturato nel contesto sincronizzato. È possibile evitare questo var res = await GetLatestPlaces(3).ConfigureAwait(false);

+0

Non riesco a utilizzare la parola chiave await perché non sono in un metodo asincrono. E non posso cambiarlo in un metodo Async Task <> perché sto sovrascrivendo il metodo 'OnActionExecuting (ActionExecutingContext filterContext)' che esiste nella classe Controller. – Juzzbott

+1

Puoi renderlo vuoto asincrono, ma non è davvero una buona idea. – rnofenko

0

Ecco il metodo che consente di chiamare i metodi asincroni in modo sincrono, per quanto riguarda la riduzione delle prestazioni e deadlock:

public static T GetResult<T>(Func<Task<T>> func) 
{ 
    var syncContext = SynchronizationContext.Current; 
    SynchronizationContext.SetSynchronizationContext(null); 

    var task = func(); 

    SynchronizationContext.SetSynchronizationContext(syncContext); 

    return task.Result; 
} 

questo modo si potrebbe chiamare

var places = GetResult(() => GetLatestPlaces(3)); 

Tuttavia, attenzione dal momento che corrente SynchronizationContext non viene acquisito, qualsiasi contesto associato a esso non scorre nell'attività. Nel tuo esempio, HttpContext.Current non sarà disponibile in GetLatestPlaces().

Problemi correlati