使用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;
}
}
答案 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()
});