使用EF Core

时间:2017-08-07 16:05:09

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

使用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。 EnumerableAdapter 即可。一旦我尝试在客户端中使用结果,应用程序就会停止响应,因为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;
    }
}

2 个答案:

答案 0 :(得分:0)

您可以从数据库中取出提取并重新整形为单独的查询,如下所示:

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

答案 1 :(得分:0)

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

我在我的应用中做了这种非常类型的嵌套投影(在我的回答中稍作修改)。如果这仍然导致您的死锁,也许您需要检查您如何调用此过程。我是从控制器到数据库的完全异步,所以也许你的问题?注意 - 我删除了Include,因为您要预测要返回的所有列,所以不应该使用它。

修改

最初我只有EF6可用,我发布的内容正在发挥作用。你是对的,EF Core似乎有不同的工作方式。我将ToList()添加到我的代码中,所以它与你的第一篇文章基本相同,但这对我来说很好,但是,执行基本上会进行n + 1个DB调用。也许它会在某个时候解决,你可以放弃内部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()
            });