使用Entity Framework异步记录计数与记录。

5

我想从数据库中获取记录,同时在同一次调用中获取记录数。我的方法是异步的,所以我不确定这是否是正确的方法:

    public async Task<IEnumerable<ProductItemViewModel>> GetAsync(int pageIndex, int pageSize)
    {
        var products = this.Repository.GetAll<Product>().OrderBy(p => p.Price).AsQueryable();

        var count = await products.CountAsync();
        if (count == 0)
            return new List<ProductItemViewModel>();

        products = products.ToPaginatedList(pageIndex, pageSize);
        var result = await products.ToListAsync();

        var viewModels = new List<ProductItemViewModel>();
        Mapper.Map(result, viewModels);

        return viewModels.AsEnumerable();
    }

我认为等待CountAsync()并不是很好,但我不确定如何实现。

谢谢。

编辑:

很抱歉我没有在任何地方返回计数,因为看起来只有那个 == 0 检查,但最终我将返回具有记录的计数,因为我需要它用于我的angular分页指令。还没有想出如何返回(新类与元组),所以我错过了它,专注于entity framework Count()和entity fetch in single DB call。


为什么需要Count?ToPaginatedList是如何工作的?为什么不直接检查result.Count == 0 - Yacoub Massad
ToPaginatedList() 只将当前 IQueriable 分页为新的 IQueriable。 - zhuber
2
通过使用 SkipTake - Yacoub Massad
2个回答

3
我认为你的代码可以优化成这样:
using AutoMapper.QueryableExtensions;
//...
public async Task<IEnumerable<ProductItemViewModel>> GetAsync(int pageIndex, int pageSize)
{
    var products = this.Repository.GetAll<Product>().OrderBy(p => p.Price);// I think you don't need to call AsQueryable(), you already are working with IQueryable
    var result=await products.Skip(pageIndex*pageSize).Take(pageSize).ProjectTo<ProductItemViewModel>().ToListAsync();
    return result;
}

最后,这可以用一行代码完成。
public async Task<IEnumerable<ProductItemViewModel>> GetAsync(int pageIndex, int pageSize)
{

    return await this.Repository.GetAll<Product>()
                                .OrderBy(p => p.Price)
                                .Skip(pageIndex*pageSize)
                                .Take(pageSize)
                                .ProjectTo<ProductItemViewModel>()
                                .ToListAsync();
}

`.ProjectTo()` 将告诉 AutoMapper 的映射引擎发出一个选择子句到 IQueryable,通知 Entity Framework 仅查询您映射的属性,就像手动将 IQueryable 投影到带有 Select 子句的 ProductItemViewModel 一样。

好的,我可以在一行中调用所有方法,但出于可读性考虑,我这样做了 ;) - ocuenca
哇,ProjectTo<>() 真不错,我之前不知道,非常感谢。你有关于记录计数查询的任何想法吗?还有,我需要 AsQueryable,因为 ToPaginatedList 是它的扩展,或者对于这个扩展方法有其他建议吗? - zhuber
OrderBy 在这种情况下默认会返回一个 IQueryable<T>,因为它所接收的参数就是这个类型,所以加上它是多余的。虽然这样做不会有任何影响,但 octavioccl 的意思是它也没有帮助到任何人。 - Matthew Haugen
OrderBy 返回了 IOrderQueryable,因此我的 IQueryable 扩展无法工作。有什么建议(除了为两者实现相同的扩展:D)?谢谢。 - zhuber
你能展示一下你的 ToPaginatedList 扩展方法吗? - ocuenca

2

糟糕,我完全忽略了你的返回类型。我以为你是在使用计数来显示TotalResultCount和你的分页集合。既然你不是这样做的,就完全删除它。

只需删除CountAsync()调用,并检查results.Count属性是否为零。这不会是你拥有的总结果数,但它会告诉你它是否为空。

public async Task<IEnumerable<ProductItemViewModel>> GetAsync(int pageIndex, int pageSize)
{
    var result = await this.Repository.GetAll<Product>()
        .OrderBy(p => p.Price)
        .ToPaginatedList(pageIndex, pageSize)
        .ToListAsync();

    if (result.Count == 0)
    {
        // This "if" really isn't necessary, as your mapper should map an
        // empty collection to an empty collection. But it is a minor
        // efficiency improvement, and it speaks to your original code.

        return Enumerable.Empty<ProductItemViewModel>();
    }

    var viewModels = new List<ProductItemViewModel>();
    Mapper.Map(result, viewModels);

    return viewModels; // AsEnumerable() has no effect here, as the cast will happen by
                       // default anyway
}

这样做可以避免对服务器的额外调用,但代价是该变量的含义。如果您的分页支持告诉用户代理有多少页(例如),那么除此之外,这段代码应该能够处理您所需的内容。如果您需要知道总结果计数,则请在下面阅读;但除此之外,这个答案存在错误的假设。听起来你已经得到了最好的解决方案。我不知道Entity Framework中是否有使用T-SQL的COUNT(*) OVER()子句的方法,这似乎正是您要寻找的。您在这里的唯一问题是将运行两个查询以获取服务器上的数据:有多少记录?给我x..y条记录。这比一次性获取它们效率低,但在大多数应用程序中可能并没有太大的区别。如果您担心这种性能成本,建议重新评估您可能拥有的索引,或者甚至考虑使用其他替代Entity Framework的方法。所有这些都是async的事实并没有任何区别。对CountAsync()调用await正是它的正确使用方式。

调用 .Count() 会有什么不同,还是我不应该这样做? - zhuber
@user3201952 这不会影响它所做的任何事情 - 它仍然是两个服务器调用。唯一的区别在于如何处理线程,而CountAsync更受欢迎。您可以在这里阅读有关“async”编程的更多信息,但要点是您想要这样做。 - Matthew Haugen
@user3201952 等等,开个玩笑。我刚刚重新阅读了你的代码。我误解了你的意图。是的,完全放弃 Count。你不需要它。我已经编辑了我的回答。 - Matthew Haugen
我会稍微编辑一下我的问题,因为你的假设是正确的。我目前还没有返回计数,但是在解决实体框架计数获取问题后会这样做... 对此感到抱歉。 - zhuber

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接