在失败时正确删除 WCF 回调的模式

发布于 2024-10-01 07:24:11 字数 1653 浏览 2 评论 0原文

我有一个存储连接实例的静态列表的类。在回调中,列表中的每个实例都会被调用,如果调用失败,该实例将从列表中删除。

细节很复杂,因为添加、删除和枚举列表项时需要同步。我目前正在使用这种模式:

class Foo : IDisposable
{
    private static readonly List<Foo> _Connections = new List<Foo> ();
    private readonly IFooCallback CallbackChannel;

    internal Foo ()
    {
        CallbackChannel = OperationContext.Current.GetCallbackChannel<IFooCallback> ();

        lock (_Connections) {
            _Connections.Add (this);
            OperationContext.Current.Channel.Closing += (s, e) => Dispose ();
            OperationContext.Current.Channel.Faulted += (s, e) => Dispose ();
        }
    }

    public void Dispose ()
    {
        lock (_Connections) {
            _Connections.Remove (this);
        }
    }

    private void RaiseCallback ()
    {
        List<Foo> connections;
        lock (_Connections) {
            connections = new List<Foo> (_Connections);
        }
        foreach (var con in connections) {
            try {
                con.CallbackChannel.SomeCallback ();
            }
            catch (CommunicationException) {
                OperationContext.Current.Channel.Abort ();
            }
            catch (TimeoutException) {
                OperationContext.Current.Channel.Abort ();
            }
        }
    }
}

我的想法:

  1. 实例的静态列表,每个实例都存储 CB 通道。
  2. 当通道关闭或中止时,实例将被删除。
  3. 在回调时,将创建(同步)并枚举(不同步)列表的副本。
  4. 当回调失败时,通道将中止。

失败的回调会导致通道中止,进而导致 Dispose 并从列表中删除。这可能会也可能不会发生在同一线程上(无法保证,因为可能会因不同的事件而随时引发 Dispose)。

我的问题是,这是处理实例存储和回调故障处理的常见模式,或者如何改进?在 RaiseCallback 中枚举之前复制列表是一个错误,还是正确的?

I've got a class that stores a static list of connection instances. On callbacks, every instance in the list is called, and if the call fails the instance is removed from the list.

Devil's in detail, since sync is needed when adding, removing and enumerating list items. I'm currently using this pattern:

class Foo : IDisposable
{
    private static readonly List<Foo> _Connections = new List<Foo> ();
    private readonly IFooCallback CallbackChannel;

    internal Foo ()
    {
        CallbackChannel = OperationContext.Current.GetCallbackChannel<IFooCallback> ();

        lock (_Connections) {
            _Connections.Add (this);
            OperationContext.Current.Channel.Closing += (s, e) => Dispose ();
            OperationContext.Current.Channel.Faulted += (s, e) => Dispose ();
        }
    }

    public void Dispose ()
    {
        lock (_Connections) {
            _Connections.Remove (this);
        }
    }

    private void RaiseCallback ()
    {
        List<Foo> connections;
        lock (_Connections) {
            connections = new List<Foo> (_Connections);
        }
        foreach (var con in connections) {
            try {
                con.CallbackChannel.SomeCallback ();
            }
            catch (CommunicationException) {
                OperationContext.Current.Channel.Abort ();
            }
            catch (TimeoutException) {
                OperationContext.Current.Channel.Abort ();
            }
        }
    }
}

My idea:

  1. Static list of instances, every instance stores the CB channel.
  2. Instance is removed when channel is closed or aborted.
  3. On callback a copy of the list is created (in sync) and enumerated (out of sync).
  4. When a callback fails the channel gets aborted.

A failed callback causes a channel abort, which in turn causes an Dispose and removal from the list. This may or may not happen on the same thread (cannot be guaranteed since a Dispose may be raised anytime due to a different event).

My question is, is this a common pattern to handle instance storing and callback fault handling, or how could it be improved? Is copying the list before enumerating in RaiseCallback a bug, or is it correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文