我使用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进行此操作?
答案 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
实施包含StartWith
和Select
的功能。我把它们移出了,所以现在Observable.Create
只会在新消息可用时返回通知。
更重要的是,在GetElement
中,现在有一个.Publish().RefCount()
电话。只要至少有一个子观察(订阅)闲置,这将使messageService
订阅保持打开状态(不会调用.Dispose()
)。