WCF - рекомендуется обратный вызов клиента? - PullRequest
2 голосов
/ 03 ноября 2011

У меня есть одна служба WCF, которая предоставляет две операции - IncrementList () и GetList (). Клиент B подключается к сервису, вызывает GetList () и отображает его пользователю. Клиент A имеет возможность обновлять этот список, вызывая IncrementList ().
Я хочу, чтобы клиент B получал уведомление при вызове IncrementList (), чтобы он мог снова вызвать GetList () для отображения обновленных данных.

Не могли бы вы описать, как бы вы это реализовали? Callbacks? Дуплекс? Издатель / Подписчик?
Что-нибудь новое в WCF 4.0, которое помогает в этом сценарии?

Service diagram

Спасибо!

1 Ответ

5 голосов
/ 03 ноября 2011

Что-то, что вы должны решить заранее, это если вы хотите выдвинуть весь список при его увеличенном (тяжелом) или просто обновленный элемент для каждого клиента. Приведенный ниже код предназначен для отправки всего списка, но его легко изменить, просто чтобы обновить обновленный объект (рекомендуется).
При запуске приложения клиент B должен позвонить Register_client, а затем GetList. Впоследствии он будет уведомлен с помощью обратного вызова при увеличении списка (клиент должен включить этот интерфейс)
Для вызова GetList требуется дуплексный канал и SessionMode.Required.

Ваш сервер должен подразумевать:

[ServiceContract(SessionMode = SessionMode.Required
                 CallbackContract = typeof(IMyCallback))]
    public interface IMyServer {

    [OperationContract]
    void Register_client();

    [OperationContract(IsOneWay = true)]
    void IncrementList();

    [OperationContract]
    ListObject[] GetList();
}  

[DataContract]
public class ListObject {
    [DataMember]...
}

Ваш клиент должен подразумевать:

public interface IMyCallback {
    [OperationContract(IsOneWay = true)]
    void PushList(ListObject[] list);
}

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

public override void Register_client() {
    // Store callback interfaces for all connected clients:
    IMyCallback callback = OperationContext.Current.GetCallbackChannel<IGatewayServerCallback>();
    if (clients.Contains(callback) == false)
    clients.Add(callback);
    Trace.WriteLine(string.Format("Client connection established ({0})", clients.Count));
}

Где:

private List<IMyCallback> clients = new List<IMyCallback>();

Реализация IncrementList должна сделать обратный вызов, чтобы отправить новый список (или, точнее, просто новый объект, который был добавлен в список) клиенту - что-то вроде:

for (int i = 0; i < clients.Count; i++) {
    if (((ICommunicationObject)clients[i]).State == CommunicationState.Opened) {
    try {
        clients[i].PushList(list);
    }
    catch (Exception e) {
        clients.RemoveAt(i--);
        Trace.WriteLine(e);
        Trace.WriteLine(string.Format("Removing client {0} (exception).", i + 1));
    }
}

Имплиментация обратного вызова (на стороне клиента) выглядит примерно так:

public class MyCallback : IMyCallback {
public void PushList(ListObject[] list) {
    // Were client side - update list code here...
}

Вероятно, для этой реализации обратного вызова требуется ссылка на некоторый объект, содержащий данные списка - возможно, это передается в конструктор (не показан).

Когда вы создаете экземпляр прокси-объекта, вам необходимо передать экземпляр обратного вызова в конструктор прокси - что-то вроде:

MyServerClient client_proxy = new MyServerClient(new InstanceContext(my_callback, binding_str)
...