简体   繁体   English

实体框架异步记录计数与记录

[英]Entity Framework async record count with records

I'm trying to fetch records from database but in the same call to DB get record count. 我正在尝试从数据库中获取记录,但在同一次调用DB中获取记录计数。 My methods are async so I'm not sure if this is the right way to do it: 我的方法是异步的,所以我不确定这是否是正确的方法:

    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();
    }

I think its not so good to await CountAsync() but I'm not sure how to implement it. 我认为等待CountAsync()不太好,但我不确定如何实现它。

Regards. 问候。

EDIT: 编辑:

I apologize for not returning count anywhere, as it looks now that I only count because of that == 0 check, but I will eventually return count with records because I need it for my angular paging directive. 我为没有在任何地方返回计数而道歉,因为它现在看起来我只计算因为== 0检查,但我最终会返回带有记录的计数,因为我需要它用于我的角度分页指令。 Haven't figured it out how I will return (new class vs tuple) so I missed it out and focused on entity framework Count() and entity fetch in single DB call. 还没弄明白我将如何返回(新类vs元组)所以我错过了它并专注于单个数据库调用中的实体框架Count()和实体提取。

I think your code can be optimized to this: 我认为您的代码可以针对此进行优化:

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;
}

At the end it can be done in just one line: 最后,它可以在一行中完成:

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();
}

The .ProjectTo<ProductItemViewModel>() will tell AutoMapper's mapping engine to emit a select clause to the IQueryable that will inform Entity Framework that it only needs to query the properties that you mapped, same as if you manually projected your IQueryable to an ProductItemViewModel with a Select clause. .ProjectTo<ProductItemViewModel>()将告诉AutoMapper的映射引擎向IQueryable发出一个select子句,该子句将通知Entity Framework它只需要查询您映射的属性,就像手动将IQueryable手动投影到ProductItemViewModel 。一个Select子句。

Oops, I totally missed your return type. 哎呀,我完全错过了你的回归类型。 I thought you were using the count to display a TotalResultCount along with your paginated collection. 我以为您使用计数来显示TotalResultCount以及您的分页集合。 Since you aren't, cut that out completely. 既然你不是,那就完全消除了。

Just drop the CountAsync() call, and check the results.Count property for zero. 只需删除CountAsync()调用,并将results.Count属性检查为零。 That won't be the total number of results you have, but it'll tell you whether it's empty or not. 这不是你拥有的结果总数,但它会告诉你它是否为空。

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
}

This avoids the extra call to the server, at the cost of that variable's meaning. 这避免了对服务器的额外调用,代价是该变量的含义。 Read below this line if your pagination supports telling the user agent how many pages there are, for example, but other than that, this code should handle what you're doing here. 如果您的分页支持告诉用户代理有多少页面,请阅读下面这一行,但除此之外,此代码应该处理您在此处所做的事情。


This answer holds an incorrect assumption that you needed to know the total result count; 这个答案有一个错误的假设,你需要知道总结果数; I originally read the code incorrectly. 我最初错误地读了代码。

It sounds like what you've got is going to be your best bet. 听起来你所拥有的将是你最好的选择。

I don't know of any way in Entity Framework to use T-SQL's COUNT(*) OVER () clause, which is really what it sounds like you're looking for. 我不知道实体框架中有什么方法可以使用T-SQL的COUNT(*) OVER ()子句,这听起来就像你正在寻找的那样。

The only trouble with your approach here is that you'll be running two queries against your server: 您的方法唯一的麻烦是您将对您的服务器运行两个查询:

  • How many records are there? 有多少条记录?
  • Give me the records x..y 给我记录x..y

That's less efficient than if you could grab them together, but it probably doesn't make a huge difference in a majority of applications. 这比你可以一起抓住它们的效率低,但它可能不会在大多数应用程序中产生巨大的差异。 If you're worried about that performance cost, I'd reevaluate indexes you may have, or even think about alternatives to Entity Framework for this. 如果您担心性能成本,我会重新评估您可能拥有的索引,或者甚至考虑实体框架的替代方案。

The fact that this is all async doesn't make any difference. 事实上,这都是async并没有任何区别。 Calling await against CountAsync() is exactly how it's meant to be used. 调用await CountAsync()正是它的用法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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