在实体框架中使用异步选择类型为IQueryable <T>的列表

Abh*_*nyu 3 c# linq lambda asynchronous entity-framework

我正在尝试将数据访问同步查询转换为异步查询,到目前为止,除了选择return的列表之外,我都进行了转换IQueryable<T>

到目前为止,这是我所做的:

    [Dependency]
    public SampleContext db { get; set; }

    public async System.Threading.Tasks.Task<Profile> Add(Profile item)
    {
        db.Profiles.Add(item);
        await db.SaveChangesAsync();
        return item;
    }

    public async System.Threading.Tasks.Task<Profile> Get(string id)
    {
        return await db.Profiles.AsNoTracking().Where(i => i.Id == id).FirstOrDefaultAsync();
    }

    public async System.Threading.Tasks.Task Remove(string id)
    {
        Profile item = db.Profiles.Find(id);
        item.IsDeleted = 1;
        db.Entry(item).State = EntityState.Modified;
        await db.SaveChangesAsync();
    }

    public async System.Threading.Tasks.Task<bool> Update(Profile item)
    {
        db.Set<Profile>().AddOrUpdate(item);
        await db.SaveChangesAsync();
        return true;
    }
Run Code Online (Sandbox Code Playgroud)

上面的代码运行良好,我被困在转换这段代码:

    public IQueryable<Profile> GetAll()
    {
        return db.Profiles.AsNoTracking().Where(i => i.IsDeleted == 0);
    }
Run Code Online (Sandbox Code Playgroud)

如何将上述代码转换为异步代码?我尝试了Stephen Cleary的示例代码,但无法弄清楚是什么ProcessEventAsync以及如何将其应用于我的代码。另外,我不能使用.ToList(),这将太昂贵了,无法将所有数据加载到内存中。

Har*_*lse 6

您必须知道查询的区别和查询的结果之间的差异。An IQueryable拥有执行查询的所有内容。这不是查询本身,创建IQueryable不会执行查询。

如果你看起来更密切合作,以LINQ语句,你会看到有两种类型:那些回报率IQueryable(和IEnumerable),以及那些回报List<TResult>TResultsTKey,等等,任何不是IQueryable/IEnumerable。如果返回值是IQueryable,则可以说该函数使用了延迟执行(或延迟执行):Expression创建了执行查询的,但尚未执行查询。

这样做的好处是,您可以连接LINQ语句,而不必对每个语句执行查询。

当您要求IQueryable获取枚举数并且是否开始枚举时,将执行查询,可以使用隐式地使用foreach,也可以使用IQueryable.GetEnumerator()和显式地使用IEnumerator.MoveNext()(也称为foreach)。

因此,只要创建查询并返回IQueryable,创建Task就没有用了。串联LINQ语句只会更改的Expression IQueryable,而您不必等待。

只有当你创建一个将实际执行查询的功能,你需要一个异步版本:ToListAsyncFirstOrDefaultAsyncMaxAsync等国内这些功能将GetEnumeratorMoveNextAsync < -这是实际的异步功能

结论:通常返回的 IQueryable<...>所有函数都不需要Async版本,所有返回实际获取的数据的函数都需要Async版本

例子。不需要异步:不执行查询:

// Query customer addresses:
static IQueryable<Address> QueryAddresses(this IQueryable<Customer> customers)
{
     return customers.Select(customer => customer.Address);
}
Run Code Online (Sandbox Code Playgroud)

需要异步:

static async Task<List<Address>> FetchAddressesAsync (this IQueryable<Customer> customers)
{
     var query = customers.QueryAddresses;   // no query executed yet
     return await query.ToListAsync();       // execute the query
     // could of course be done in one statement
}

static async Task<Address> FetchAddressAsync(this.IQueryable<Customer> customers, int customerId)
{
    var query = customers.Where(customer => customer.Id == customerId)
                         .FetchAddresses();
    // no query executed yet!
    // execute:
    return await query.FirstOrDefaultAsync();
}
Run Code Online (Sandbox Code Playgroud)

用法:

int customerId = ...
using (var dbContext = new InvoiceContext())
{
     Address fetchedCustomerAddress = await dbContext.Customers
         .FetchAddressAsync(customerId);
}
Run Code Online (Sandbox Code Playgroud)

在极少数情况下,您必须枚举自己,您将在MoveNextAsync

IQueryable<Customer> myCustomers = ...
IEnumerator<Customer> customerEnumerator = myCustomers.GetEnumerator();

while (await customerEnumerator.MoveNextAsync())
{
     Customer customer = customerEnumerator.Current;
     Process(customer);
}
Run Code Online (Sandbox Code Playgroud)