在EF Core(OwnerMany)中加载集合项目



如何查询和筛选OwnsMany函数为其所有者配置的集合属性中的数据?

这是我试过的样品:

using (var context = new BloggingContext())
{
context.Database.OpenConnection();
context.Database.EnsureCreated();
context.Blogs.Add(sampleBlog);
context.SaveChanges();
var blog = context.Blogs.Single(b => b.BlogId == 1);
var goodPosts = context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Where(p => p.Title == "...")
.ToList();
}

和型号类别:

public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}

和dbContext类:

public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().OwnsMany(x => x.Posts, post =>
{
post.ToTable("Posts");
post.HasKey("Id");
post.Property(x => x.Title);
post.Property(x => x.Content);
});
modelBuilder.Entity<Blog>()
.ToTable("Blogs");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connection = new SqliteConnection("Data Source=:memory:");
optionsBuilder.UseSqlite(connection);
}
}

我得到了这个例外:

System.Reflection.TargetInvocationException:"调用的目标引发了异常。">

内部异常:

ArgumentException:"System.Collections.Generic.List`1[Post]"类型的表达式不能用于返回类型"Post">

不幸的是,您遇到了EF Core错误/缺陷/问题,更具体地说,是在他们为所属实体类型集合实现的Query()方法中。

它在目前最新的官方EF Core 3.1.7版本和EF Core 5预览版中都是可复制的,因此值得向他们的GitHub Issue Tracker报告。Query()之后的代码无关紧要,该问题仅用再现

context.Entry(blog).Collection(b => b.Posts).Query();

现在是解决方法。所讨论的方法主要用于显式加载。但是,对所属实体类型的导航总是被急切地加载,这可能就是为什么在添加所属实体的类型的集合功能时,它们错过了这种情况(它适用于常规的一对多导航和常规/所属引用导航(。不管是什么原因,在他们修复之前,您不应该使用Query()方法,而是使用LINQ手动组成查询。例如,使用以下等效的示例Query方法:

context.Blogs.Where(b => b.BlogId == blog.BlogId).SelectMany(b => b.Posts)

但现在它强加了另一个错误/要求:

跟踪查询项目拥有的实体在结果中没有相应的所有者。拥有的实体在没有所有者的情况下无法跟踪。在结果中包含所有者实体,或者使用AsNoTracking()使查询不跟踪。

因此,您还需要将AsNoTracking()添加到goodPosts查询中。

或者,在这种特殊情况下,由于拥有的实体集合已经与所有者一起加载/跟踪,因此使用LINQ to Objects查询内存中的物化集合,这是通过将Query()替换为CurrentValue:来实现的

var goodPosts = context.Entry(blog)
.Collection(b => b.Posts)
.CurrentValue
.Where(p => p.Title == "...")
.ToList();

或者直接使用导航属性:

var goodPosts = blog.Posts
.Where(p => p.Title == "...")
.ToList();

@Ivan Stoev的精彩回答。

关于AsNoTracking((:

如果您想更新父实体和/或其子实体,请使用此

var blog = context.Blogs.FirstOrDefault(b => b.BlogId == 1);
//Manipulating the blog here.
context.SaveChange();

但是,如果你只需要阅读数据,而不需要更新博客,那么就使用这个

var blog = context.Blogs.AsNoTracking().FirstOrDefault(b => b.BlogId == 1);
//What ever code you have here.

你可以查看的一篇很好的附加文章是EF Core 3.0中包含的这一突破性变化。

编码快乐,加油!

最新更新