C# 使用实体框架6.1.3的异步分页

C# 使用实体框架6.1.3的异步分页,c#,entity-framework,asynchronous,C#,Entity Framework,Asynchronous,我是EF的新手,所以如果有什么事情做得不对,我会提前道歉。我正在努力使分页与EF6异步工作 我已经按照本文实现了分页机制:,我认为它是干净的,而且非常到位(但也不是完美的),但是我不能让它异步工作,这是一个问题 根据文章,我创建了以下界面: public interface IPageList { int TotalCount { get; } int PageCount { get; } int Page { get; } int PageSize { get;

我是EF的新手,所以如果有什么事情做得不对,我会提前道歉。我正在努力使分页与EF6异步工作

我已经按照本文实现了分页机制:,我认为它是干净的,而且非常到位(但也不是完美的),但是我不能让它异步工作,这是一个问题

根据文章,我创建了以下界面:

public interface IPageList
{
    int TotalCount { get; }
    int PageCount { get; }
    int Page { get; }
    int PageSize { get; }
}
我创建了这个类:

public class PageList<T> : List<T>, IPageList
{
    public int TotalCount { get; private set; }
    public int PageCount { get; private set; }
    public int Page { get; private set; }
    public int PageSize { get; private set; }

    public PageList(IQueryable<T> source, int page, int pageSize)
    {
        TotalCount = source.Count();
        PageCount = GetPageCount(pageSize, TotalCount);
        Page = page < 1 ? 0 : page - 1;
        PageSize = pageSize;
        AddRange(source.Skip(Page * PageSize).Take(PageSize).ToList());
    }

    private int GetPageCount(int pageSize, int totalCount)
    {
        if (pageSize == 0)
            return 0;

        var remainder = totalCount % pageSize;
        return (totalCount / pageSize) + (remainder == 0 ? 0 : 1);
    }
}
仍然没有解决我的异步问题。我目前正在阅读这篇文章,希望能对以下方面有所帮助:

更新-2

我想我已经弄明白了,但它仍然没有我想要的那么灵敏,所以我不能100%确定它是否正确。我以为在我的WPF应用程序中切换到我的日志选项卡时,切换会是即时的,但事实并非如此

总之,我改变了以下几点:

    public async Task<List<LogEntity>> GetLogsAsync(int pageNumber, int pageSize)
    {
        using (_dbContext = new DatabaseContext())
        {
            var results = _dbContext.Logs.OrderBy(o=>o.DateTime).ToPageList(pageNumber, pageSize).Select(l => new LogEntity
            {
                LogId = l.LogId,
                Message = l.Message,
            }).AsAsyncQueryable();

            return await results.ToListAsync();
        }
    }
我得到以下错误:

中发生“System.NotSupportedException”类型的异常 EntityFramework.SqlServer.dll,但未在用户代码中处理 附加信息:实体或复杂类型 “MyCompany.DataLayerSql.LogEntity”不能在LINQ to中构造 实体查询

this.TotalCount=source.Count()上


有什么想法吗?

您在这里错误地使用了
async
。除非您正在执行I/O或非常长的操作,否则在创建、管理和合并线程时,通常只会产生额外的开销

从数据库查询是一个I/O操作,但是您还没有了解实体框架的行为,因此您缺少使此操作异步的好处

实体框架(通常是LINQ)使用一种称为的技术。在这种情况下,这意味着在您想要对数据执行操作之前,不会向数据库发送任何内容。您可以有条件地将
.Where()
.Skip()
等添加到您的核心内容中,EF将坐在那里准备构建SQL查询

要将该SQL语句发送到数据库,需要对其执行操作,在
页面列表中执行两次。第一个是:

TotalCount = source.Count();
它将SQL与所有
WHERE
语句等一起,在
SELECT COUNT(*)前面加上前缀,并获取结果

第二次在这里:

AddRange(source.Skip(Page * PageSize).Take(PageSize).ToList());
在上行末尾,
.ToList()
将向数据库发送另一个查询,检索您请求的所有列和行,并填充所有实体这是您想要异步的地方,但是

您的替代方法是放弃构造函数中的所有设置,而是使用一个方法,该方法可以轻松地设置为
async

在最初的问题中,您从以下内容开始:

_dbContext.Logs.Select(l => new
    {
        LogId = l.LogId,
        Message = l.Message,
    })
    .OrderBy(o => o.DateTime)
此后,您还更新了将
OrderBy()
.ToPageList()
放在
.Select()之前。但是,您仍然将其作为匿名对象进行查询,因此您再次需要在需要时继续强制转换

回到问题的根源,我们需要查看您的返回声明:

return await results.AsQueryable<LogEntity>().ToListAsync();
这才是你真正需要的

如果您死心塌地地使用
async
,那么您应该通过添加默认构造函数和以下方法来返工您的类:

public async Task CreateAsync(IQueryable<T> source, int page, int pageSize)
{
    TotalCount = await source.CountAsync(); // async here would help
    PageCount = GetPageCount(pageSize, TotalCount);
    Page = page < 1 ? 0 : page - 1;
    PageSize = pageSize;
    AddRange(await source.Skip(Page * PageSize)
                         .Take(PageSize)
                         .ToListAsync()); // async here too!
}
public async Task CreateAsync(IQueryable源代码,int-page,int-pageSize)
{
TotalCount=wait source.CountAsync();//这里的异步会有所帮助
PageCount=GetPageCount(pageSize,TotalCount);
第页=第<1页?0页:第-1页;
PageSize=页面大小;
AddRange(等待源代码。跳过(页面*页面大小)
.Take(页面大小)
.ToListSync());//这里也是异步的!
}
这可以通过重构来解决,但这就是要点。那么就这样称呼它:

// Get your query set up, but don't execute anything on it yet.
var results = _dbContext.Logs.Select(l => new LogEntity
                                    {
                                        LogId = l.LogId,
                                        l.Message
                                    })
                             .OrderBy(l => l.DateTime);

var pageList = new PageList<LogEntity>();
await pageList.Create(results, pageNumber, pageSize);

return pageList;
//设置查询,但不要对其执行任何操作。
var results=\u dbContext.Logs.Select(l=>newlogentity
{
LogId=l.LogId,
l、 信息
})
.OrderBy(l=>l.DateTime);
var pageList=新的页面列表();
等待页面列表。创建(结果、页码、页面大小);
返回页面列表;

看起来您不需要
页面列表
类,因为您没有使用它。还有2
Select
s的原因是什么?@IvanStoev我正在使用它。它在GetLogsAsync函数的第10行使用。至于2个选择,这是一个好问题,我不想在这里问多个问题。我刚刚又做了一次尝试,并且简化了它。我马上上传。不使用它意味着你没有返回
PagedList
,这是该课程的全部目的。如果您想要简单的分页结果,只需在查询中包含
Skip
/
Take
。e、 g.
return await\u dbContext.Logs.OrderBy(o=>o.DateTime)。选择(…)。跳过((pageNumber-1)*pageSize)。获取(pageSize.ToListSync()@IvanStoev,这是在构造函数中完成的,只有在没有
async
@IvanStoev的情况下,您才是正确的。我完全错过了这一部分,这是一个重要的部分!现在我将回顾一下我的代码,看看如何返回这个,因为如果我想根据返回的数据禁用按钮,那么返回列表是不够的,这是我在文章中指出的一点不完美,但我是需要学习如何阅读的人:)感谢详细的回答。很抱歉耽误了回来的时间,但我正在努力解决另一个我从问题中漏掉的部分。我需要将LogEntity(数据库模型)转换为日志对象(域模型)。当我尝试你的“最简单”建议时,我会得到以下错误:
实体或复杂类型“MyCompany.DataLaye”
TotalCount = source.Count();
AddRange(source.Skip(Page * PageSize).Take(PageSize).ToList());
_dbContext.Logs.Select(l => new
    {
        LogId = l.LogId,
        Message = l.Message,
    })
    .OrderBy(o => o.DateTime)
return await results.AsQueryable<LogEntity>().ToListAsync();
return _dbContext.Logs
                 .Select(l => new LogEntity // Cast here so your .ToPageList
                              { // will start as the object type you want.
                                  LogId = l.LogId,
                                  Message = l.Message
                              })
                 .OrderBy(l => l.DateTime)
                 .ToPageList(pageNumber, pageSize);
public async Task CreateAsync(IQueryable<T> source, int page, int pageSize)
{
    TotalCount = await source.CountAsync(); // async here would help
    PageCount = GetPageCount(pageSize, TotalCount);
    Page = page < 1 ? 0 : page - 1;
    PageSize = pageSize;
    AddRange(await source.Skip(Page * PageSize)
                         .Take(PageSize)
                         .ToListAsync()); // async here too!
}
// Get your query set up, but don't execute anything on it yet.
var results = _dbContext.Logs.Select(l => new LogEntity
                                    {
                                        LogId = l.LogId,
                                        l.Message
                                    })
                             .OrderBy(l => l.DateTime);

var pageList = new PageList<LogEntity>();
await pageList.Create(results, pageNumber, pageSize);

return pageList;