El SynchronizationContext actual no se puede usar como TaskScheduler

Estoy usando tareas para ejecutar llamadas de servidor en ejecución larga en mi ViewModel y los resultados se TaskScheduler.FromSyncronizationContext() en Dispatcher usando TaskScheduler.FromSyncronizationContext() . Por ejemplo:

 var context = TaskScheduler.FromCurrentSynchronizationContext(); this.Message = "Loading..."; Task task = Task.Factory.StartNew(() => { ... }) .ContinueWith(x => this.Message = "Completed" , context); 

Esto funciona bien cuando ejecuto la aplicación. Pero cuando ejecuto mis pruebas de NUnit en Resharper obtengo el mensaje de error en la llamada a FromCurrentSynchronizationContext como:

El SynchronizationContext actual no se puede usar como TaskScheduler.

Supongo que esto se debe a que las pruebas se ejecutan en subprocesos de trabajo. ¿Cómo puedo asegurarme de que las pruebas se ejecuten en el hilo principal? Cualquier otra sugerencia es bienvenida.

Debe proporcionar un SynchronizationContext. Así es como lo manejo:

 [SetUp] public void TestSetUp() { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); } 

La solución de Ritch Melton no funcionó para mí. Esto se debe a que mi función TestInitialize es asíncrona, al igual que mis pruebas, por lo que con cada await se pierde el actual SynchronizationContext . Esto se debe a que, como señala MSDN, la clase SynchronizationContext es “tonta” y simplemente pone en cola todo el trabajo en el grupo de subprocesos.

Lo que funcionó para mí en realidad es omitir la llamada FromCurrentSynchronizationContext cuando no hay un SynchronizationContext (es decir, si el contexto actual es nulo ). Si no hay un subproceso de interfaz de usuario, no necesito sincronizarme con él en primer lugar.

 TaskScheduler syncContextScheduler; if (SynchronizationContext.Current != null) { syncContextScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } else { // If there is no SyncContext for this thread (eg we are in a unit test // or console scenario instead of running in an app), then just use the // default scheduler because there is no UI thread to sync with. syncContextScheduler = TaskScheduler.Current; } 

Encontré esta solución más directa que las alternativas, que fueron:

  • Pase un TaskScheduler al ViewModel (a través de la dependency injection)
  • Cree un SynchronizationContext prueba y un hilo de interfaz de usuario “falso” para que se ejecuten las pruebas, lo cual es mucho más problemático para mí que valga la pena

Perdí algunos de los matices de subprocesos, pero no estoy probando explícitamente que mis callbacks OnPropertyChanged se activen en un hilo específico, así que estoy de acuerdo con eso. Las otras respuestas que usan el new SynchronizationContext() realmente no mejoran para ese objective de todos modos.

He combinado varias soluciones para tener la garantía de trabajar SynchronizationContext:

 using System; using System.Threading; using System.Threading.Tasks; public class CustomSynchronizationContext : SynchronizationContext { public override void Post(SendOrPostCallback action, object state) { SendOrPostCallback actionWrap = (object state2) => { SynchronizationContext.SetSynchronizationContext(new CustomSynchronizationContext()); action.Invoke(state2); }; var callback = new WaitCallback(actionWrap.Invoke); ThreadPool.QueueUserWorkItem(callback, state); } public override SynchronizationContext CreateCopy() { return new CustomSynchronizationContext(); } public override void Send(SendOrPostCallback d, object state) { base.Send(d, state); } public override void OperationStarted() { base.OperationStarted(); } public override void OperationCompleted() { base.OperationCompleted(); } public static TaskScheduler GetSynchronizationContext() { TaskScheduler taskScheduler = null; try { taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } catch {} if (taskScheduler == null) { try { taskScheduler = TaskScheduler.Current; } catch {} } if (taskScheduler == null) { try { var context = new CustomSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(context); taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); } catch {} } return taskScheduler; } } 

Uso:

 var context = CustomSynchronizationContext.GetSynchronizationContext(); if (context != null) { Task.Factory .StartNew(() => { ... }) .ContinueWith(x => { ... }, context); } else { Task.Factory .StartNew(() => { ... }) .ContinueWith(x => { ... }); }