繁体   English   中英

转换IDictionary <T, Task<bool> &gt;到IObservable <KeyValuePair<T, bool> &gt;

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM