2011-01-21 10 views
9

¿Hay alguna razón para hacer que cada servicio WCF llame a Async?¿Hay alguna razón para hacer que cada WCF llame a Async?

Estaba teniendo esta discusión con mi compañero. Le gustaría hacer que cada servicio WPF llame a Async para evitar bloquear la UI (es una aplicación WPF de escritorio). Estoy en contra de esta idea No creo que se necesite una llamada asincrónica en la mayoría de los casos, y cuando se necesita tanto el RequestingClass como el DataManager deben codificarse específicamente para manejar la llamada Async.

Mi argumento para esto es que hay mucho más código para configurar devoluciones de llamadas para todo y es muy confuso. También creo que esto podría causar una disminución en el rendimiento, aunque todavía no lo he verificado. Su argumento es que a veces está recuperando una gran cantidad de datos y bloquearía la interfaz de usuario, y no es demasiado trabajo configurar las llamadas de WCF como esta (tampoco encuentra el siguiente código confuso).

Ambos nunca hemos trabajado con el servidor WCF antes, así que pensé que podría darle el beneficio de la duda y preguntar aquí por otras opiniones.

Por ejemplo:

mi manera:

public override User GetById(int id) 
{ 
    return new User(service.GetUserById(id)); 
} 

que se bloquee la interfaz de usuario, UserDataManager, y el canal de servicio WCF hasta que el servidor WCF regresa con el DataTransferObject usuario, sin embargo, es fácil de entender y rápido de codificar. Se usaría para la mayoría de las llamadas de servicio WCF a menos que realmente esperara un retraso en la obtención de datos, en cuyo caso el DataManager se configuraría para manejar llamadas Async.

su camino cola de

public override void GetById(int id, Action<UserGroup> callback = null) 
{ 
    // This is a queue of all callbacks waiting for a GetById request 
    if (AddToSelectbyIdQueue(id, callback)) 
     return; 

    // Setup Async Call 
    var wrapper = new AsyncPatternWrapper<UserDTO>(
     (cb, asyncState) => server.BeginGetUserById(id, cb, asyncState), 
     Global.Instance.Server.EndGetUserById); 

    // Hookup Callback 
    wrapper.ObserveOnDispatcher().Subscribe(GetByIdCompleted); 

    // Run Async Call 
    wrapper.Invoke(); 
} 

private void GetByIdCompleted(UserDTO dto) 
{ 
    User user = new User(dto); 

    // This goes through the queue of callbacks waiting 
    // for this method to complete and executes them 
    RunSelectIdCallbacks(user.UserId, user); 
} 

de devolución de llamada de clase base:

/// <summary> 
/// Adds an item to the select queue, or a current fetch if there is one 
/// </summary> 
/// <param name="id">unique object identifier</param> 
/// <param name="callback">callback to run</param> 
/// <returns>False if it needs to be fetched, True if it is already being 
/// fetched</returns> 
protected virtual bool AddToSelectbyIdQueue(int id, Action<T> callback) 
{ 
    // If the id already exists we have a fetch function already going 
    if (_selectIdCallbacks.ContainsKey(id)) 
    { 
     if(callback != null) 
      _selectIdCallbacks[id].Add(callback); 
     return true; 
    } 

    if (callback != null) 
    { 
     List<Action<T>> callbacks = new List<Action<T>> {callback}; 
     _selectIdCallbacks.Add(id, callbacks); 
    } 

    return false; 
} 

/// <summary> 
/// Executes callbacks meant for that object Id and removes them from the queue 
/// </summary> 
/// <param name="id">unique identifier</param> 
/// <param name="data">Data for the callbacks</param> 
protected virtual void RunSelectIdCallbacks(int id, T data) 
{ 
    if (_selectIdCallbacks.ContainsKey(id)) 
    { 
     foreach (Action<T> callback in _selectIdCallbacks[id]) 
      callback(data); 

     _selectIdCallbacks.Remove(id); 
    } 
} 

No bloquear la interfaz de usuario, el DataManager, o el canal de servicio WCF, sin embargo, una gran cantidad de exceso la codificación entra en eso.

El AsyncPatternWrapper está en nuestra aplicación independientemente. Es algo que nos permite realizar llamadas asíncronas WCF y suscribimos un evento de devolución de llamada

EDITAR Tenemos un envoltorio que podemos utilizar desde el subproceso de interfaz de usuario para envolver cualquier llamada DataManager. Ejecuta el método sincrónico en un BackgroundWorker y ejecuta una devolución de llamada en contra de los resultados.

La mayoría del código adicional es para evitar bloquear el DataManager y el canal de servicio WCF.

+3

Considere la posibilidad de evaluar la versión CTP de la nueva característica C# "async/await". Está diseñado para hacer que escribir este tipo de código sea mucho más fácil. Estaremos encantados de recibir sus comentarios en el foro asincrónico. http://msdn.microsoft.com/en-us/vstudio/async.aspx –

+0

@Eric: ¡Gracias, lo estoy viendo ahora y hasta ahora me está gustando! – Rachel

+0

@Eric: ¿Me puede indicar algún buen tutorial/tutorial sobre cómo usar esto con un servicio de WCF? – Rachel

Respuesta

6

Su pareja es correcta; no deberías bloquear el hilo de UI.

Como alternativa a las llamadas asincrónicas, también puede realizar llamadas sincrónicas en una cadena de fondo utilizando un BackgroundWorker o ThreadPool.

+0

Tenemos un contenedor que hace eso ... ejecuta la llamada de DataManager en un subproceso BackgroundWorker para detener el bloqueo de la interfaz de usuario, sin embargo, eso se llama desde el hilo de la interfaz de usuario no el DataManager (la razón es que WPF no puede alterar los objetos creados en el hilo de UI de un BackgroundWorker). Me preocupa toda la codificación adicional para simplemente dejar de bloquear el DataManager y el canal WCF. Actualizaré mi pregunta. – Rachel

+0

¿Todavía recomendarías la 2da manera? No tengo inconveniente en hacerlo si es la mejor forma de hacer las cosas, pero no quiero complicar demasiado las cosas si no son necesarias – Rachel

+0

@Rachel: Debería seguir usando un hilo separado para las llamadas al servicio web, solo use Dispatcher.Invoke() para actualizar la interfaz de usuario cuando regresa la llamada: mucha información a través de Google. –

2

Algunas cosas vale la pena destacar:

  1. Cualquier llamada WCF puede bloquear potencialmente hasta que su tiempo de espera y usted debe tener esto en cuenta. Es una experiencia de usuario deficiente cuando la UI está bloqueada en el caso de una conexión perdida.Las llamadas también pueden ejecutarse más lentamente de lo normal, si, por ejemplo, el servidor está atascado.

  2. Incluso si no está ajustando las llamadas para fines asincrónicos, probablemente ya desee envolver las llamadas al proxy. Esto se debe a que cualquier llamada en el proxy puede potencialmente poner el canal en estado de error y, si esto ocurre, tiene para llamar a Abort() en el canal o puede perder recursos. Consulte this post y this other post para obtener más información. Los proxies WCF no se pueden usar como clases normales y necesariamente implican envoltorios adicionales si desea usarlos en un escenario de producción. Esto es en gran medida inevitable, ya que es una consecuencia de los casos límite adicionales y comportamientos impredecibles introducidos por los requisitos de comunicación remota.

+0

Quité una gran cantidad del código para poder mostrar solo el código relevante. Todas las llamadas de servicio en los DataManagers están envueltas en algo que atrapa excepciones/fallas y asegura que el canal permanezca abierto. La mayoría de las llamadas a DataManager están envueltas en algo que las ejecuta en un BackgroundWorker y detiene el bloqueo de UI. Buen consejo, aunque :) – Rachel

Cuestiones relacionadas