Канал неисправностей исключительной ситуации WCF

Я не нашел четкого ответа на этот вопрос. так что если уже есть вопрос по этому поводу, мой плохой.

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

Теперь я где-то читал, что если вы включите надежные сеансы, то исключение не приведет к неисправности канала. Это правда? Как я могу предотвратить переход канала в неисправное состояние, когда исключение происходит при одностороннем вызове?

2 ответа

Список зарегистрированных и доступных клиентов, которые вы можете хранить на каком-либо ресурсе, например, "Список".
Создайте другой интерфейс, который предоставляет методы Connect/Disconnect. Connect вызывается, когда приложение запускается и в методе клиент добавляется в список. Disconnect, в свою очередь, вызывается, когда приложение закрывается, чтобы избавить клиента от списка. События OnStartup/OnClosing или их эквиваленты, в зависимости от типа клиента приложения, относятся к моменту запуска и закрытия приложения. Такое решение гарантирует, что ресурс хранит только доступных пользователей.

[ServiceContract]
interface IConnection
{
    [OperationContract(IsOneWay = true)]
    void Connect();

    [OperationContract(IsOneWay = true)]
    void Disconnect();
}

[ServiceContract]
interface IServiceCallback
{
    [OperationContract(IsOneWay = true)]
    void CallbackMethod();
}

[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
    [OperationContract]
    void DoSth();
}

class YourService : IConnection, IService
{
    private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();

    public void Connect()
    {
        var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
        if (Clients.All(client => client != newClient))
            Clients.Add(newClient);
    }

    public void Disconnect()
    {
        var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
        if (Clients.Any(cl => cl == client))
            Clients.Remove(client);
    }

    public void DoSth()
    {
        foreach(var client in Clients)
            client.CallbackMethod();
    }
}

В конце представьте другую конечную точку с IConnection, чтобы клиент мог создать прокси, предназначенный для использования только для соединения / разъединения.

РЕДАКТИРОВАТЬ:

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

Чтобы добавить то, что сказал Максимус.

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

вот код, надеюсь, это поможет!

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
    private struct SystemState
    {
        public string State;
        public string ExtraInfo;
    }

    private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();

    private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();

    public void RegisterClient(string name, string system)
    {
        lock (mCallbacks)
        {
            IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();

            Key<string> key = new Key<string>(name, system);

            if (!mCallbacks.ContainsKey(key))
            {
                mCallbacks.Add(key, callback);
            }
            else
            {
                mCallbacks[key] = callback;
            }

            foreach (KeyValuePair<string, SystemState> s in mStates)
            {
                mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
            }
        }
    }

    public void UnregisterClient(string name)
    {
        lock (mCallbacks)
        {
            outer:  foreach (var key in mCallbacks.Keys)
            {
                if (key.Key1 == name)
                {
                    mCallbacks.Remove(key);
                    goto outer;
                }
            }
        }
    }

    public void SetState(string system, string state, string extraInfo)
    {
        lock (mCallbacks)
        {
            List<Key<string>> toRemove = new List<Key<string>>();

            SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
            SystemState systemState;
            if (!mStates.TryGetValue(system, out systemState))
                mStates.Add(system, s);
            else
                mStates[system] = s;

            foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
            {
                try
                {
                    callback.Value.ServiceCallback(system, state, extraInfo);
                }
                catch (CommunicationException ex)
                {
                    toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
                }
                catch
                {
                    toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
                }
            }

            foreach (Key<string> key in toRemove)
                mCallbacks.Remove(key);
        }
    }
}
Другие вопросы по тегам