Канал неисправностей исключительной ситуации 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);
}
}
}