【问题标题】:Asynchronous projecting entities with child collection properties using EF Core使用 EF Core 的具有子集合属性的异步投影实体
【发布时间】:2017-08-08 09:25:49
【问题描述】:

使用 EF Core,我想异步获取具有 ChildModel 集合属性的 FooModel 列表。

public class FooModel
{    
    public Guid Id { get; set; }    
    public string Name { get; set; }
    public List<ChildModel> Childs { get; set; }
}

虽然同步版本返回没有问题,但异步版本会导致应用程序冻结。

//Async version.
public static async Task<List<FooModel>> ListFooModelAsync()
{
    using (var db = new AppDbContext())
    {
        var foo_items = await db.Foos
            .Include(e => e.Childs)
            .Select(e => new FooModel
            {
                Id = e.Id,            
                Name = e.Name,
                Childs = e.Childs.Select(
                    child => new ChildModel { Id = child.Id, Name = child.Name })
                    .ToList()
            })
            .ToListAsync()
            .ConfigureAwait(false);
        return foo_items;
    }
}

我认为对 Childs 的 ToList() 调用导致管道某处出现死锁。

如果我删除 Childs 构造行中的 .ToList(),它不会冻结并返回 FooModel 列表,但其 Childs 集合的类型将是 Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider。枚举适配器。一旦我尝试在客户端中使用结果,应用程序就会停止响应,这可能是因为 EF 尝试解析 Childs 集合,但此时没有可用的 DbContext。

关于如何解决这个问题的任何想法?

//Sync version works fine.
public static List<FooModel> ListFooModel()
{
    using (var db = new AppDbContext())
    {
        var foo_items = db.Foos
            .Include(e => e.Childs)
            .Select(e => new FooModel
            {
                Id = e.Id,            
                Name = e.Name,
                Childs = e.Childs.Select(
                    child => new ChildModel { Id = child.Id, Name = child.Name })
                    .ToList()
            })
            .ToList();
        return foo_items;
    }
}

【问题讨论】:

    标签: c# .net entity-framework asynchronous async-await


    【解决方案1】:

    您可以像这样将从数据库中提取并重新塑造成单独的查询:

        public static async Task<List<FooModel>> ListFooModelAsync()
        {
            using (var db = new AppDbContext())
            {
                var foo_items = await db.Foos
                    .Include(e => e.Childs)
                    .ToListAsync();
    
                var results = foo_items
                    .Select(e => new FooModel
                    {
                        Id = e.Id,
                        Name = e.Name,
                        Childs = e.Childs.Select(
                            child => new ChildModel { Id = child.Id, Name = child.Name })
                            .ToList()
                    }).ToList();
    
                return results;
            }
        }
    

    【讨论】:

    • 这可以解决这个问题。我唯一遇到的问题是它会提供完整的对象图,这是我想避免的。
    【解决方案2】:
    public static async Task<List<FooModel>> ListFooModelAsync()
    {
        using (var db = new AppDbContext())
        {
            var foo_items = await db.Foos
                .Select(e => new FooModel
                {
                    Id = e.Id,            
                    Name = e.Name,
                    Childs = e.Childs.Select(
                        child => new ChildModel { Id = child.Id, Name = child.Name })
                }).ToList()
                .ToListAsync();
            return foo_items;
        }
    }
    

    我正在我的应用程序中进行这种类型的嵌套投影(在我的回答中稍作修改)。如果这仍然给您造成死锁,那么您可能需要检查您是如何调用此过程的。我从控制器到数据库完全异步,所以也许是你的问题?注意-我删除了包含,您不需要它,因为您正在投影要返回的所有列。

    编辑:

    最初我只有 EF6 可用并且我发布的内容正在运行。你是对的,EF Core 的工作方式似乎有所不同。我将 ToList() 添加到我的代码中,因此它与您的第一篇文章基本相同,但这对我来说很好,但是,执行基本上会进行 n+1 次数据库调用。也许它会在某个时候得到解决,您可以删除内部 ToList。扩展 David Browne 所说的另一个想法是进行两个单独的投影查询并快速加入它们,因此基本上只有 2 个 DB 读取,如下所示:

    var foo_outer = await db.Foos
                    .Select(e => new FooModel
            {
                Id = e.Id,            
                Name = e.Name,
                Childs = new List<ChildModel>()
            }).ToListAsync();
    
    
    var foo_inner = await db.Childs
                    .Where(x => foo_outer.Select(y => y.id).Contains(x.FoosForeignKey))
                    .Select(x => new
                    {
                        Id = x.Id,
                        Name = x.Name,
                        FooKey= x.FoosForeignKey
                    }).ToListAsync();
    
    var foo_items= foo_outer.Select(x => new
                {
                    Id = x.Id,
                    Name = x.Name,
                    Childs = foo_inner.Where(y => y.FooKey == x.Id).ToList()
                }); 
    

    【讨论】:

    • 调用非常困难,向前 FooList = await MyService.ListFooModelAsync()。不幸的是,排除 Include 并没有什么不同。使用 EF Core 是否正确?
    猜你喜欢
    • 2018-11-06
    • 2019-09-07
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 2017-04-18
    相关资源
    最近更新 更多