简体   繁体   中英

WCF oneway exception faults channel

I haven't found a clear answer on this. so if there is already a question about this, my bad.

I have a WCF service that pushes data via a callback method to connected clients. this callback method is oneway. so everytime there is new data I loop over the connected users and push the data. The problem I have right now is when a client disconnects it throws an error and the channel becomes faulted. I always thought that oneway didn't care if the message arrives at the destination. So if there's no client, then bad luck. but no exception. but there is an exception and that exception faults the channel.

Now I've read somewhere that if you enable reliable sessions, that the exception won't fault the channel. Is this true? How can I prevent that the channel goes into faulted state when an exception happens on a oneway call?

The list of registered and avaiable clients you can store in some resource such as List.
Create another interface which exposes Connect/Disconnect methods. Connect is invoked when application starts off and within method client is added to the list. Disconnect in turn is invoked when application shuts down in order to get rid client of list. OnStartup/OnClosing events or their equivalents, depending on what kind of application client is, refer to moment when application is launched and closed. Such a solution ensures that resource stores only users avaiable to be reached.

[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();
    }
}

At the end expose another endpoint with IConnection so that client can create proxy meant to be used only for connection/disconnection.

EDIT:

I know it has been a while since I posted an answear but I did not find in order to prepare an example. The workaround is to let service's interface derive IConnection and then expose only service as an endpoint. I attach simple example of WCF and WPF app as client. Client's application violates MVVM pattern but in this case it is irrelevant. Download it here .

To add on what Maximus said.

I've implemented this pattern in a class where clients can subscribe to get updates of internal states of a system, so a monitoring client can show graphs and other clients do other stuff like enabling/disabling buttons if some state is active. It removes faulted channels from the list when they fail. Also all current states are sent when a client connects.

here's the code, hope it helps!

[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);
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM