如果订户不是来源

时间:2017-04-03 11:03:43

标签: c# system.reactive observable

我使用IObservable推送数据更新/更改,我有一个方法可以从数据库GetLatestElement获取最新数据,每当有人调用UpdateElement并且数据得到更新时,消息通过消息传递系统分发。

所以我创建了一个发出最新值的observable,然后在从消息传递系统收到更新事件时重新发出新值:

public IObservable<IElement> GetElement(Guid id)
{
    return Observable.Create<T>((observer) =>
    {
        observer.OnNext(GetLatestElement(id));

        // subscribe to internal or external update notifications
        var messageCallback = (message) =>
        {
            // new update message recieved,
            observer.OnNext(GetLatestElement(id));
        }
        messageService.SubscribeToTopic(id, messageCallback);

        return Disposable.Create(() => Console.Writeline("Observer Disposed"));
    });
}

我的问题是这是无限期的。这些更新可能会永远发生。由于我试图使系统尽可能无状态,因此为GetElementType的每个请求创建了一个新的Observable。这意味着生命周期由订户决定,而不是数据来源。

我永远不会在Observable中调用OnComplete(),我想在观察者/用户完成时完成。

但是,我需要在某个时间点调用messageService.Unsubscribe(messageCallback);,以便在完成Observable时取消订阅消息。

我可以在处理订阅时执行此操作,但之后我只能订阅一次,这似乎可能会引入错误。

如何使用Observable进行此操作?

2 个答案:

答案 0 :(得分:1)

似乎对Observable.Create如何运作存在一些误解。每当您对Subscribe的结果致电GetElement()时,都会执行Observable.Create的正文。因此,对于每个订阅者,您对messageService进行单独订阅,并执行单独的回调。如果您取消订阅 - 您只会删除订阅者的订阅。所有其他人都保持活跃,因为他们有自己的messageCallback。当然假设messageService已正确实施。以下示例应用程序说明:

static IElement  GetLatestElement(Guid id) {
    return new Element();
}

public class Element : IElement {

}

public interface IElement {

}

class MessageService {
    private Dictionary<Guid, Dictionary<Action<IElement>, CancellationTokenSource>> _subs = new Dictionary<Guid, Dictionary<Action<IElement>, CancellationTokenSource>>();
    public void SubscribeToTopic(Guid id, Action<IElement> callback) {
        var ct = new CancellationTokenSource();
        if (!_subs.ContainsKey(id))
            _subs[id] = new Dictionary<Action<IElement>, CancellationTokenSource>();
        _subs[id].Add(callback, ct);
        Task.Run(() =>
        {
            while (!ct.IsCancellationRequested) {
                callback(new Element());
                Thread.Sleep(500);
            }
        });
    }

    public void Unsubscribe(Guid id, Action<IElement> callback) {
        _subs[id][callback].Cancel();
        _subs[id].Remove(callback);
    }
}

public static IObservable<IElement> GetElement(Guid id)
{
    var messageService = new MessageService();
    return Observable.Create<IElement>((observer) =>
    {
        observer.OnNext(GetLatestElement(id));

        // subscribe to internal or external update notifications
        Action<IElement> messageCallback = (message) =>
        {
            // new update message recieved,
            observer.OnNext(GetLatestElement(id));
        };
        messageService.SubscribeToTopic(id, messageCallback);

        return Disposable.Create(() => {
            messageService.Unsubscribe(id, messageCallback);
            Console.WriteLine("Observer Disposed");
        });
    });
}

public static void Main(string[] args) {
    var ob = GetElement(Guid.NewGuid());
    var sub1 = ob.Subscribe(c =>
    {
        Console.WriteLine("got element");
    });

    var sub2 = ob.Subscribe(c =>
    {
        Console.WriteLine("got element 2");
    });
    // at this point we see both subscribers receive messages
    Console.ReadKey();
    sub1.Dispose();
    // first one is unsubscribed, but second one is still alive
    Console.ReadKey();
}

正如我所说的那样 - 我认为没有理由在这种情况下完成你的观察。

答案 1 :(得分:1)

正如Evk指出的那样,Observable.Create运行然后几乎立即处置。如果您想保持messageService订阅打开,Rx可以帮助您。看MessageObservableProvider。剩下的就是编译:

public class MessageObservableProvider
{
    private MessageService messageService;
    private Dictionary<Guid, IObservable<Unit>> _messageNotifications = new Dictionary<Guid, IObservable<Unit>>();
    private IObservable<Unit> GetMessageNotifications(Guid id)
    {
        return Observable.Create<Unit>((observer) =>
        {
            Action<Message> messageCallback = _ => observer.OnNext(Unit.Default);
            messageService.SubscribeToTopic(id, messageCallback);

            return Disposable.Create(() =>
            {
                messageService.Unsubscribe(messageCallback);
                Console.WriteLine("Observer Disposed");
            });
        });
    }

    public IObservable<IElement> GetElement(Guid id)
    {
        if(!_messageNotifications.ContainsKey(id))
            _messageNotifications[id] = GetMessageNotifications(id).Publish().RefCount();

        return _messageNotifications[id]
            .Select(_ => GetLatestElement(id))
            .StartWith(GetLatestElement(id));
    }

    private IElement GetLatestElement(Guid id)
    {
        throw new NotImplementedException();
    }
}

public class IElement { }
public class Message { }
public class MessageService
{
    public void SubscribeToTopic(Guid id, Action<Message> callback)
    {
        throw new NotImplementedException();
    }

    public void Unsubscribe(Action<Message> callback)
    {
        throw new NotImplementedException();
    }
}

您的原始Create实施包含StartWithSelect的功能。我把它们移出了,所以现在Observable.Create只会在新消息可用时返回通知。

更重要的是,在GetElement中,现在有一个.Publish().RefCount()电话。只要至少有一个子观察(订阅)闲置,这将使messageService订阅保持打开状态(不会调用.Dispose())。