Рекомендации по преобразованию вызовов WCF в асинхронные вызовы WCF

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

private void btnSave_Click(object sender, RoutedEventArgs e)
{

  List<Item> itemList = GetList();
  foreach(Item i in itemList)
  {
    DoSomeWork(i);

    if(i.SomeID == 0)
    {      
       DoSomeMoreWork(i);  
    }

    UpdateRecord(i)  // this can't execute until the above code is complete

  }
}

private void DoSomeWork(Item i)
{
  // call async method
}

private void DoSomeMoreWork(i)
{
  // call async method
}

private void UpdateRecord(item i)
{
  // call async method
}

Каков наилучший способ рефакторинга кода, чтобы он работал асинхронно, или мне нужно полностью переосмыслить свою логику? Мне действительно нужно повсюду вставлять счетчики и переключатели, чтобы убедиться, что определенные действия выполняются до того, как будут выполняться другие?

РЕДАКТИРОВАТЬ: причина, по которой я это делаю, заключается в том, что в ближайшие пару месяцев мы преобразуем это приложение WPF в Silverlight, для которого требуются асинхронные вызовы. Итак, я пытаюсь преобразовать наши обычные вызовы WCF в асинхронные в процессе подготовки. Я считаю, что это требует другого образа мышления.


person ScottG    schedule 10.03.2009    source источник


Ответы (6)


Для того, что вы делаете, я бы сказал, что реальное место для обработки вещей - сделать один вызов службы для каждого элемента, а не 3.

Желательно, если список пунктов не велик, сделать один звонок в сервис со всем списком...

private void btnSave_Click(object sender, RoutedEventArgs e)
{  
    List<Item> itemList = GetList();  
    foreach(Item i in itemList)  
    {    
        DoAllTheWorkAndUpdate(i);    
    }
}

or...

private void btnSave_Click(object sender, RoutedEventArgs e)
{  
    List<Item> itemList = GetList();  
    foreach(Item i in itemList)  
    {    
        if(i.Id == 0)
        {
            DoLotsOfWorkAndUpdate(i);
        }
        else
        {
            DoSomeWorkAndUpdate(i);
        }

    }
}

or...

private void btnSave_Click(object sender, RoutedEventArgs e)
{  
    List<Item> itemList = GetList();  
    DoTheWorkOnTheWholeList(itemList);
}

Другими словами, кажется, что некоторые из ваших обязанностей могут быть неуместны — я обычно предпочитаю создавать службы, в которых я могу сделать один звонок к ним. Затем асинхронный характер не имеет значения, поскольку вы не выполняете последовательность событий.

person kyoryu    schedule 08.08.2009
comment
гм, а что, если вы вызываете 3 отдельные службы на 3 отдельных серверах? ну, скорее всего, 2, но, поскольку я играю в адвоката дьявола, я скажу 3. например, мне нужно создать профиль оплаты в веб-службе CIM Authorize.NET, а затем отправить результат в мою собственную службу. - person Simon_Weaver; 09.01.2010
comment
Что ж, в таком случае вам, очевидно, придется сделать не менее n вызовов, где n — количество отдельных сервисов. Я бы все же попытался ограничиться одним вызовом для каждой отдельной службы. Кроме того, если вы передаете данные из одной службы в другую, это может быть признаком неуместного распределения обязанностей — лучше, если одна из служб свяжется с другой вместо вас. Конечно, это не всегда возможно, но требовать, чтобы потребительский код поддерживал синхронизацию двух других сервисов, — это то, что нужно делать осторожно — много места для ошибок. - person kyoryu; 11.01.2010

Взгляните на Джувала Лоуи (автор книги Программирование служб WCF) веб-сайт с примерами реализации асинхронного программирования в ВКФ. Загрузки бесплатны; вам просто нужно указать свой адрес электронной почты.

person Matt Davis    schedule 11.03.2009

Я, возможно, немного озадачен тем, почему вам нужно использовать асинхронные операции WCF, когда вам нужно, чтобы вещи были синхронными внутри цикла.

Если вы просто используете асинхронные методы, чтобы предотвратить зависание пользовательского интерфейса, вы можете просто использовать BackgroundWorker, который поддерживает обновления хода выполнения, чтобы поддерживать пользовательский интерфейс в актуальном состоянии, а не использовать асинхронные вызовы WCF.

Вы также должны иметь возможность вызывать различные функции из событий Completed для асинхронных методов.

Просто подключите обработчики событий к завершенным событиям, а затем передайте объект Item в качестве параметра userState при запуске асинхронного вызова WCF. Таким образом, вы будете иметь его в качестве параметра, когда срабатывает каждое из событий Completed. Таким образом, вы будете выполнять следующий шаг обработки только после завершения предыдущего асинхронного вызова.

Я не знаю, действительно ли это отвечает на ваш вопрос.

person Navaar    schedule 10.03.2009
comment
По крайней мере, одна причина для использования асинхронных методов, когда вы действительно предпочитаете синхронные: когда вы работаете в Silverlight, все должно быть асинхронным. Обычно это делается по уважительным причинам, но это это PITA. - person Ken Smith; 08.08.2009
comment
+1 за комментарий Кена Смита; Я столкнулся с той же стеной stackoverflow.com/questions/1286864/ - person Andrei Rînea; 17.08.2009

Попробуйте использовать это

http://ayende.com/Blog/archive/2008/03/29/WCF-Async-without-proxy.aspx

подход, который определенно работает.

person Andrei Sedoi    schedule 14.06.2010

Если вы не используете Silverlight, вы можете заблокировать поток в одном методе до завершения других методов, например, с помощью события ManualResetEvent. Но это не будет работать в Silverlight, так как все вызовы WCF происходят в основном потоке пользовательского интерфейса, поэтому, если вы заблокируете этот поток, блокируется все. Лучший подход — сделать что-то подобное, используя обратные вызовы:

    public delegate void OperationCallback();

    private void btnSave_Click(object sender, RoutedEventArgs e)
    {

        List<Item> itemList = GetList();
        foreach (Item i in itemList)
        {
            DoSomeWork(i, () =>
            {
                if (i.SomeID == 0)
                {
                    DoSomeMoreWork(i, () =>
                    {
                        UpdateRecord(i);
                    });
                }
                else
                {
                    UpdateRecord(i);
                }
            });

        }
    }

    private void DoSomeWork(Item i, OperationCallback callback)
    {
        // call async method then callback when it completes.
        callback();
    }

    private void DoSomeMoreWork(Item i, OperationCallback callback)
    {
        // call async method, then callback when it completes.
        callback();
    }

    private void UpdateRecord(Item i)
    {
        // call async method
    }

Это, конечно, не так ясно, как в синхронной версии, но если вы как можно больше используете лямбда-выражения, все же можно сохранить достаточно читаемый поток управления.

person Ken Smith    schedule 08.08.2009

Добавьте 2 свойства в Item с именами SomeWorkDone и SomeMoreWorkDone как логические значения. Создайте методы для обработки как DoSomeWorkCompleted, так и DoSomeMoreWorkCompleted. В этих методах задайте для соответствующих логических свойств значение true и вызовите UpdateRecord. В UpdateRecord убедитесь, что оба свойства Done имеют значение true, а затем завершите вызовы.

У вас будут некоторые возможные проблемы с конкуренцией, но это должно помочь вам.

person Chris Porter    schedule 08.07.2009
comment
Если я не ошибаюсь, я не думаю, что это сработает. Вам либо нужно заблокировать один метод до тех пор, пока другой метод (ы) не завершится (в этом случае вы используете какой-либо ResetEvent), либо вам нужно вызвать один метод, когда другой метод завершается в своего рода обратном вызове. - person Ken Smith; 08.08.2009