Depende del tipo de aplicación que está escribiendo, por ejemplo, ¿acepta errores? ¿Cuáles son sus requisitos de datos, en tiempo real? ¿ácido? clientes eventualmente consistentes y/o parcialmente conectados/a veces desconectados?
Tenga en cuenta que existe una distinción entre concurrencia y asincronía. Puede tener asynchronocity y, por lo tanto, invocar el intercalado de llamadas de método sin tener realmente un programa de ejecución simultánea.
Una idea podría ser tener un lado de lectura y escritura de la aplicación, donde el lado de escritura publica eventos cuando se ha modificado. Esto podría conducir a un sistema impulsado por eventos: el lado de lectura se generaría a partir de los eventos publicados y podría reconstruirse. La interfaz de usuario podría ser impulsada por tareas, en el sentido de que una tarea para realizar produciría un comando que su BL tomaría (o capa de dominio si así lo desea).
Un siguiente paso lógico, si tiene lo anterior, es ir también de origen de evento. Luego, recrearías el estado interno del modelo de escritura a través de lo que se había comprometido previamente. Hay un grupo de google sobre CQRS/DDD que podría ayudarte con esto.
Con respecto a la actualización de la IU, he encontrado que las interfaces IObservable en System.Reactive, System.Interactive, System.CoreEx son adecuadas. Le permite omitir diferentes contextos de invocación simultáneos: despachador, grupo de subprocesos, etc., e interopera bien con la Biblioteca de tareas paralelas.
También debería considerar dónde ubica su lógica comercial: si utiliza el dominio, le diría que podría ponerlo en su aplicación, ya que tendría un procedimiento de actualización para los binarios que distribuye De todos modos, cuando llega el momento de actualizar, también existe la opción de ponerlo en el servidor. Los comandos pueden ser una buena manera de realizar las actualizaciones en el lado de escritura y una unidad de trabajo conveniente cuando el código orientado a la conexión falla (son pequeños y serializables y la interfaz de usuario se puede diseñar a su alrededor).
Para dar un ejemplo, echar un vistazo a this thread, con este código, que añade una prioridad a la IObservable.ObserveOnDispatcher (...) - llaman:
public static IObservable<T> ObserveOnDispatcher<T>(this IObservable<T> observable, DispatcherPriority priority)
{
if (observable == null)
throw new NullReferenceException();
return observable.ObserveOn(Dispatcher.CurrentDispatcher, priority);
}
public static IObservable<T> ObserveOn<T>(this IObservable<T> observable, Dispatcher dispatcher, DispatcherPriority priority)
{
if (observable == null)
throw new NullReferenceException();
if (dispatcher == null)
throw new ArgumentNullException("dispatcher");
return Observable.CreateWithDisposable<T>(o =>
{
return observable.Subscribe(
obj => dispatcher.Invoke((Action)(() => o.OnNext(obj)), priority),
ex => dispatcher.Invoke((Action)(() => o.OnError(ex)), priority),
() => dispatcher.Invoke((Action)(() => o.OnCompleted()), priority));
});
}
El ejemplo anterior se podría utilizar como esto blog entry discute
public void LoadCustomers()
{
_customerService.GetCustomers()
.SubscribeOn(Scheduler.NewThread)
.ObserveOn(Scheduler.Dispatcher, DispatcherPriority.SystemIdle)
.Subscribe(Customers.Add);
}
... Así, por ejemplo, con una tienda de starbucks virtual, que tendría una entidad de dominio que tiene algo así como una clase 'barista', que produce eventos 'CustomerBoughtCappuccino': {costos: '$ 3', marca de tiempo: '2011-01-03 12: 00: 03.334556 GMT + 0100 ', ... etc}. Su lado de lectura se suscribiría a estos eventos. El lado de lectura podría ser un modelo de datos: para cada una de las pantallas que presentan datos, la vista tendría una clase de modelo de vista única, que se sincronizaría con la vista en un diccionario observable like this. El repositorio sería (: IObservable), y sus presentadores se suscribirían a todo eso, o solo a una parte de él. De esa manera su interfaz gráfica de usuario podría ser:
- tarea impulsada -> comando impulsado BL, con especial atención en las operaciones del usuario
- asíncrono
- lectura-escritura-segregada
Teniendo en cuenta que el BL sólo toma comandos y no muestra un modelo de lectura lo suficientemente bueno para todas las páginas, puede hacer que la mayoría de las cosas en él sean internas, internas y privadas, lo que significa que puede usar System.Contracts para demostrar que no tiene ningún error en él (!). Produciría eventos que leería su modelo de lectura. Puede tomar los principios principales de Caliburn Micro sobre la orquestación de flujos de trabajo de tareas asincrónicas entregadas (IAsyncResults).
Hay algunos Rx design guidelines que puede leer. Y cqrsinfo.com sobre fuentes de eventos y cqrs. Si de verdad está interesado en ir más allá de la esfera de programación asincrónica en la esfera de programación concurrente, Microsoft ha lanzado un pozo written book gratis, sobre cómo programar dicho código.
Espero que ayude.
Sé cómo se comporta Control.Invoke(), pero la pregunta es sobre el patrón de diseño y sobre las mejores prácticas. ¿Qué control utilizo para Control.Invoke()? ¿Es una mejor práctica usar el formulario? ¿Significa que necesito una referencia al formulario en mi presentador, o llamo a Invoke() desde el código subyacente del formulario? –
@IIya: Ok, lo entiendo. Depende de las circunstancias, pero normalmente escribirá una lógica para actualizar un control, y usará el control objetivo para verificar InvokeRequired y luego control.Invoke(). Ver mis ediciones –