[英]Convert IDictionary<T, Task<bool>> to IObservable<KeyValuePair<T, bool>>
我有一個函數來替換存儲庫(例如數據庫)中的單個實體與另一個實體。 這將返回一個Task,其結果表明替換是否成功。
// Single Replace
Task<bool> Replace(Entity e, Entity Other);
現在說我想創建相同的功能,但允許替換幾個實體。 我對函數簽名的想法是:
// Multiple Replace
IObservable<KeyValuePair<Entity, bool>> Replace(IDictionary<Entity, Entity> Map);
Map包含一個字典,其鍵是要替換的實體,其值是將替換舊實體的新實體。
此Map字典的每個KeyValuePair應返回KeyValuePair <Entity,bool>,其中鍵對應於Map.Key(=將被替換的Entitiy),而Value是指示替換是否成功的bool。
我想將它作為流返回,因此我選擇了IObservable <KeyValuePair <Entity,bool >>,為每個可用的結果推出KeyValuePair <Entity,bool>。
現在,我只想讓“Multiple Replace”功能使用“Single Replace”功能進行轉換; 但是如何進行這些調用並以所需格式返回結果?
Task<bool> Replace(Entity e, Entity Other)
{
// ... do the work ...
}
IObservable<KeyValuePair<Entity, bool>> Replace(IDictionary<Entity, Entity> Map)
{
// How this should work:
// for each KeyValuePair in Map, call the Single Replace function
// and - once available - return its result (bool) mapped to the Entity in the
// resulting Observable stream
IDictionary<Entity, Task<bool>> dict = Map.ToDictionary(x => x.Key, x => Replace(x.Key, x.Value));
// .. now what? How can I now convert the dict into an IObservable<KeyValuePair<Entity,bool>> ?
}
非常感謝任何指針!
如果沒有看到一個很好的Minimal,Complete和Verifiable示例 ,它清楚地顯示了您正在做的事情,特別是您打算如何實現從您的方法返回的IObservable<T>
對象,則無法提供具體的建議。 但是,我可以提供一些建議,至少應該指出你有用的方向。
首先,我建議有一個Task<Tuple<Entity, bool>>
(或Task<KeyValuePair<Entity, bool>>
更有用,雖然我不是非常喜歡使用字典相關類型的非字典 - 依賴代碼...即你真正只是配對而不是真正的鍵/值配對。 這樣,當任務完成時,您可以從任務本身知道您需要的所有信息。
這可以通過簡單的輔助方法完成,例如:
async Task<Tuple<Entity, bool>> ReplaceAsync(Entity e, Entity other)
{
return Tuple.Create(e, await Replace(e, other));
}
我在這里使用了Tuple
,但當然你可以為此目的創建一個命名類型,這樣做實際上可能有助於代碼的可讀性。
完成后,您可以枚舉輸入字典中的所有項目(或其他更合適的集合:)),然后使用Task.WhenAny()
迭代刪除並發布每個已完成的項目:
IObservable<Tuple<Entity, bool>> Replace(IDictionary<Entity, Entity> Map)
{
Task<Tuple<Entity, bool>>[] tasks = new Task<Tuple<Entity, bool>>(Map.Count);
int i = 0;
foreach (var kvp in Map)
{
tasks[i++] = ReplaceAsync(kvp.Key, kvp.Value);
}
// Create the IObservable object somehow. Make sure it has the
// array of tasks in it. E.g. (see below for TaskObservable example)
TaskObservable<Tuple<Entity, bool>> observable =
new TaskObservable<Tuple<Entity, bool>>(tasks);
// Now, start running the observable object. Note: not really all that great
// to ignore the returned Task object but without more context in your question
// I can't offer anything more specific. You will probably want to store the
// Task object *somewhere*, await it, wrap all that in try/catch, etc. to
// make sure you are correctly monitoring the progress of the task.
var _ = observable.Run();
return observable;
}
IObservable<T>
可能看起來像:
class TaskObservable<T> : IObservable<T>
{
private class ObserverItem : IDisposable
{
public readonly IObserver<T> Observer;
public readonly TaskObservable<T> Owner;
public ObserverItem(IObserver<T> observer, TaskObservable<T> owner)
{
Observer = observer;
Owner = owner;
}
public void Dispose()
{
if (!Owner._observerItems.Contains(this))
{
throw new InvalidOperationException(
"This observer is no longer subscribed");
}
Owner._observerItems.Remove(this);
}
}
private readonly Task<T>[] _tasks;
private readonly List<ObserverItem> _observerItems = new List<ObserverItem>();
public TaskObservable(Task<T>[] tasks)
{
_tasks = tasks;
}
public IDisposable Subscribe(IObserver<T> observer)
{
ObserverItem item = new ObserverItem(observer, this);
_observerItems.Add(item);
return item;
}
private void Publish(T t)
{
foreach (ObserverItem item in _observerItems)
{
item.Observer.OnNext(t);
}
}
private void Complete()
{
foreach (ObserverItem item in _observerItems)
{
item.Observer.OnCompleted();
}
}
async Task Run()
{
for (int i = 0; i < _tasks.Length; i++)
{
Task<T> completedTask = await Task.WhenAny(tasks);
Publish(completedTask.Result);
}
Complete();
}
}
基於Peter的解決方案,利用Task.ToObservable進行一些修改:
IObservable<Tuple<Entity, bool>> Replace(IDictionary<Entity, Entity> Map)
{
Task<Tuple<Entity, bool>>[] tasks = new Task<Tuple<Entity, bool>>(Map.Count);
int i = 0;
foreach (var kvp in Map)
{
tasks[i++] = ReplaceAsync(kvp.Key, kvp.Value);
}
return tasks
.Select(x => x.ToObservable())
.Merge(); // or use .Concat for sequential execution
}
似乎工作正常,有沒有人發現任何潛在的問題?
使用.Merge提供一個IObservable,它可以並行啟動任務,在完成任務時提供結果,並在完成所有任務后完成。
或者,我可以使用.Concat()按順序執行它們,即首先啟動第一個任務,等待它完成,然后啟動第二個任務,等等。
好的一點是,我相信錯誤處理是免費的,因此每當任何任務拋出異常時,就會自動調用IObservable的OnError。
我不確定我是否遺漏了任何東西,但我認為這可能適合你:
public IObservable<KeyValuePair<T, bool>> Convert<T>(IDictionary<T, Task<bool>> source)
{
return
Observable
.Create<KeyValuePair<T, bool>>(o =>
(
from s in source.ToObservable()
from v in s.Value.ToObservable()
select new KeyValuePair<T, bool>(s.Key, v)
).Subscribe(o));
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.