Return Task<IReadOnlyCollection<T>> 从 EF Core 查询而不等待?

Return Task<IReadOnlyCollection<T>> from EF Core query without await?

我希望我的类型尽可能具体。在提供封装查询的方法时,通常会看到 List<T> returned(或更具体地说 Task<List<T>>。)难怪 EF Core 的 .ToListAsync() 方法。但这不是很准确,是吗? List<T> 是 SQL 服务器(或您正在使用的任何持久性)中更大集合的投影。不可修改。

我想从我的方法中 return IReadOnlyCollection<T>。这似乎更准确。我正在 return 从我的源数据中投影,您不能通过修改投影来修改源数据。我的结果也不会被错误地修改而不回退到 List<T>,在这一点上你已经走得太远了我假设你知道你在做什么。

所以我尝试像同步一样return界面。

public Task<IReadOnlyCollection<TResult>> GetResultAsync() =>
  _dbContext.Where(x => x.Property == true).ToListAsync();

我在这里避免使用 asyncawait 运算符。我想未经等待地传递任务以避免 await 开销。但是当包裹在 Task<T> 中时,C# 的类型系统无法识别包裹的 List<T> 可转换为 IReadOnlyCollection<T>。所以 Task<List<T>> 不能转换为 Task<IReadOnlyCollection<T>>。当然,您可以尝试显式强制转换,但出于同样的原因,编译器也不喜欢那样。

嗯,这很烦人。但是我们可以很容易地用 async wait 剥离 Task<T>

public async Task<IReadOnlyCollection<TResult>> GetResultAsync() =>
  await _dbContext.Where(x => x.Property = true).ToListAsync();

为了避免添加代码,我考虑了一种扩展方法。

public static async Task<IReadOnlyCollection<T>> ToReadOnlyCollectionAsync<T>(this IQueryable<T> query) =>
  await query.ToListAsync();

这从我的方法中获取了 await,但当然仍然添加了另一个等待。有没有办法从 EF Core 2.1 为 "free" return Task<IReadOnlyCollection<T>>

不知道 ToListAsync() 的作用,但按名称进行操作,如果我猜对了,它会迭代结果并将它们转换为后台线程上的列表和 returns 任务在生成列表之前必须 awaited

如果是这样,你的问题是

how to convert Task<List<T>> to Task<IReadonlyCollection<T>>.

如果是这样,理论上你可以这样做:

    public Task<IReadOnlyCollection<string>> ToReadOnlyCollection()
    {
        return ToListAsync().ContinueWith(x => new ReadOnlyCollection<string>(x.Result) as IReadOnlyCollection<string>);
    }

    public Task<IReadOnlyList<string>> ToReadOnlyList()
    {
        // if you don't mind an IReadOnlyList, you can use this
        // one which doesn't involve creating a new collection
        return ToListAsync().ContinueWith(x => x.Result as IReadOnlyList<string>);
    }

    private Task<List<string>> ToListAsync()
    {
        return Task.Run(() =>
        {
            Task.Delay(1000);
            return new List<string>
            {
                "1",
                "2",
                "3"
            };
        });
    }

我不相信 Task<T> 允许反方差,所以 Task<TChild>(其中 Child : Parent)不能自动转换为 Task<TParent> 所以你需要这样做你自己。