Есть ли причина, чтобы каждый WCF вызывал Async? - PullRequest
8 голосов
/ 21 января 2011

Есть ли причина, чтобы каждый сервис 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.

Ответы [ 2 ]

5 голосов
/ 21 января 2011

Ваш партнер правильный;Вы не должны блокировать поток пользовательского интерфейса.

В качестве альтернативы асинхронным вызовам вы также можете выполнять синхронные вызовы в фоновом потоке, используя BackgroundWorker или ThreadPool.

2 голосов
/ 21 января 2011

Несколько вещей, на которые стоит обратить внимание:

  1. Любой вызов WCF может потенциально блокироваться до истечения времени ожидания, и вы должны принять это во внимание. Это ухудшает работу пользователя, когда пользовательский интерфейс блокируется в случае потери соединения. Вызовы также могут выполняться медленнее, чем обычно, если, например, сервер зависает.

  2. Даже если вы не переносите вызовы в асинхронных целях, вы, вероятно, уже захотите некоторый перенос вызовов в прокси. Это связано с тем, что любой вызов прокси-сервера потенциально может перевести канал в неисправное состояние, и, если это произойдет, у вас будет для вызова Abort () на канале, или вы можете утечь ресурсы. См. этот пост и этот другой пост для получения дополнительной информации. Прокси WCF нельзя использовать, как обычные классы, и они обязательно требуют дополнительного переноса, если вы хотите использовать их в производственном сценарии. Это в значительной степени неизбежно, так как это является следствием дополнительных граничных случаев и непредсказуемого поведения, введенного требованиями удаленной связи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...