我有一个函数用另一个实体替换存储库(例如数据库)中的单个实体。这将返回一个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&lt;实体,bool&gt;,其中键对应于Map.Key(=将被替换的Entitiy),而Value是一个bool,表示替换是否成功。
我想将此作为流返回,因此我选择了IObservable&lt; KeyValuePair&LT;实体,布尔&gt; &gt;,推出KeyValuePair&lt;实体,布尔&gt;对于每个可用的结果。
现在,我只想拥有&#34; Multiple Replace&#34;功能使用&#34; Single Replace&#34;转换功能;但是如何进行这些调用并以所需格式返回结果?
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>> ?
}
非常感谢任何指针!
答案 0 :(得分:1)
如果没有看到清楚显示您正在做的事情的好Minimal, Complete, and Verifiable example,尤其是您打算如何实现从您的方法返回的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();
}
}
答案 1 :(得分:1)
基于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。
答案 2 :(得分:1)
我不确定我是否错过任何内容,但我认为这可能适合您:
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));
}