Есть ли причина, чтобы каждый сервис WCF вызывал Async?
Я обсуждал это с моим партнером.Он хотел бы сделать каждый вызов службы WPF Async, чтобы избежать блокировки пользовательского интерфейса (это настольное приложение WPF).Я против этой идеи.Я не чувствую, что в большинстве случаев требуется асинхронный вызов, и когда это необходимо, и RequestingClass, и DataManager должны кодироваться специально для обработки асинхронного вызова.настроить обратные вызовы для всего, и это очень запутанно.Я также думаю, что это может привести к снижению производительности, хотя я еще не проверял это.Его аргумент заключается в том, что иногда вы получаете много данных обратно, и это блокирует пользовательский интерфейс, и не так много работы для настройки вызовов WCF, как этот (он также не находит следующий код сбивающим с толку).
Мы оба никогда раньше не работали с сервером WCF, поэтому я решил дать ему преимущество сомнения и спросить здесь другие мнения.
Например:
Мой путь:
public override User GetById(int id)
{
return new User(service.GetUserById(id));
}
Он блокирует UI, UserDataManager и канал службы WCF до тех пор, пока сервер WCF не вернется с User DataTransferObject, однако это легко понять и быстрокод.Он будет использоваться для большинства вызовов службы WCF, если только он не ожидает задержки в получении данных, и в этом случае DataManager будет настроен для обработки асинхронных вызовов.
His Way:
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);
}
Очередь обратного вызова в базовом классе:
/// <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);
}
}
Он не блокирует интерфейс пользователя, DataManager или канал службы WCF, однако в него добавляется много дополнительного кодирования.
AsyncPatternWrapper находится в нашем приложении независимо.Это то, что позволяет нам делать вызовы Async WCF и подписывать событие обратного вызова
EDIT У нас есть обертка, которую мы можем использовать из потока пользовательского интерфейса для переноса любого вызова DataManager.Он выполняет метод Synchronous в BackgroundWorker и выполняет обратный вызов для результатов.
Большая часть дополнительного кода предназначена для предотвращения блокировки DataManager и канала службы WCF.