簡體   English   中英

如何僅包含相關實體的選定屬性

[英]How to include only selected properties on related entities

我只能包括相關實體。

using (var context = new BloggingContext()) 
{ 
    // Load all blogs, all related posts
    var blogs1 = context.Blogs 
                       .Include(b => b.Posts) 
                       .ToList(); 
}

但是,我不需要整個 BlogPost 實體。 我只對特定的屬性感興趣,例如:

using (var context = new BloggingContext()) 
{ 
    // Load all blogs, all and titles of related posts
    var blogs2 = context.Blogs 
                       .Include(b => b.Posts.Select(p => p.Title) //throws runtime exeption
                       .ToList(); 

    foreach(var blogPost in blogs2.SelectMany(b => b.Posts))
    {
        Console.Writeline(blogPost.Blog.Id); //I need the object graph
        Console.WriteLine(blogPost.Title); //writes title
        Console.WriteLine(blogPost.Content); //writes null
    }
}

您要么使用Include加載整個實體,要么將您需要的內容投影到.Select

var blogs2 = context.Blogs 
    .Select(x => new 
    {
        BlogName = x.BlogName, //whatever
        PostTitles = x.Post.Select(y => y.Title).ToArray()
    }) 
   .ToList(); 

或者,您可以執行以下操作:

var blogs2 = context.Blogs 
    .Select(x => new 
    {
        Blog = x,
        PostTitles = x.Post.Select(y => y.Title).ToArray()
    }) 
   .ToList(); 

當您不需要整個孩子時, Select總是更好,因為它可以防止查詢不需要的數據。

你可以試試這個:

using (var context = new BloggingContext())
{
    var blogProps = context.Blogs
        .SelectMany(b => 
            b.Posts.Select(p => 
                new { Blog = b, PostTitle = p.Title }
            )
         )
        .ToList();
}

編輯
如果你想堅持你的數據模型,你可以嘗試這樣的事情:

using (var context = new BloggingContext())
{
    var blogProps = context.Blogs
        .Select(b => 
            new Blog 
            { 
                Name = b.Name, 
                Posts = new List<Post>(b.Posts.Select(p => 
                    new Post 
                    { 
                        Title = p.Title 
                    })
            }
        )
        .ToList();
}

事實上,您想要的是:將一個實體拆分為一個公共的、代表性的部分和一個您並不總是想從數據庫中提取的特殊部分。 這並不是一個罕見的要求。 想想產品和圖像、文件及其內容,或者擁有公共和私人數據的員工。

實體框架核心支持兩種方式來實現:擁有類型和表拆分。

自有類型

擁有的類型是包裝在另一種類型中的類型。 它只能通過其所有者訪問。 這是它的樣子:

public class Post
{
    public int ID { get; set; }
    public Blog Blog { get; set; }
    public string Title { get; set; }
    public PostContent Content { get; set; }
}

public class PostContent
{
    public string Content { get; set; }
}

以及擁有的類型映射:

modelBuilder.Entity<Post>().OwnsOne(e => e.Content);

Blog在哪里

public class Blog
{
    public Blog()
    {
        Posts = new HashSet<Post>();
    }
    public int ID { get; set; }
    public string Name { get; set; }

    public ICollection<Post> Posts { get; set; }
}

但是,根據文檔

查詢所有者時,默認情況下將包含擁有的類型。

這意味着像這樣的語句...

var posts = context.Posts.ToList();

......總是會得到你的帖子及其內容。 因此,擁有類型可能不適合您。 我還是提到了,因為我發現當Posts are Included ...

var blogs = context.Blogs.Include(b => b.Posts).ToList();

...擁有的類型PostContent包括在內(免責聲明:我不確定這是錯誤還是功能......)。 在這種情況下,當應包含擁有的類型時,需要ThenInclude

var blogs = context.Blogs.Include(b => b.Posts)
        .ThenInclude(p => p.Content).ToList();

因此,如果Post總是通過Blog查詢,則擁有類型可能是合適的。

我不認為這在這里適用,但是當擁有類型的孩子與他們的父母有識別關系時(經典示例: Order-OrderLine )。

表拆分

通過表拆分,數據庫表被拆分為兩個或多個實體。 或者,從對象方面:兩個或多個實體映射到一個表。 模型幾乎相同。 唯一的區別是PostContent現在有一個必需的主鍵屬性( ID ,當然與Post.ID具有相同的值):

public class Post
{
    public int ID { get; set; }
    public Blog Blog { get; set; }
    public string Title { get; set; }
    public PostContent Content { get; set; }
}

public class PostContent
{
    public int ID { get; set; }
    public string Content { get; set; }
}

和表拆分映射:

modelBuilder.Entity<Post>()
    .HasOne(e => e.Content).WithOne()
    // or .WithOne(c => c.Post) if there is a back reference
    .HasForeignKey<PostContent>(e => e.ID);
modelBuilder.Entity<Post>().ToTable("Posts");
modelBuilder.Entity<PostContent>().ToTable("Posts");

現在,默認情況下Post將始終在沒有其內容的情況下進行查詢。 PostContent應該始終是Include()顯式。

此外,現在可以在沒有所有者Post情況下查詢PostContent

var postContents = context.Set<PostContent>().ToList();

我認為這正是你要找的。

當然,如果您在想要獲取沒有內容的帖子時始終使用投影,則可以不使用這些映射。

我認為有一種更簡單的方法可以做到這一點。 投影很好,但是如果您想要來自父實體的所有列而其中大部分來自子實體怎么辦? 當這些類型具有很多屬性時,使用投影意味着您需要編寫大量代碼行來選擇您想要的所有內容,除了少數您不需要的。 好吧,因為使用投影意味着您的實體不會被跟蹤,所以使用.AsNoTracking()然后清空您不想要的東西要容易得多。

var foos = await _context.DbSet<Foo>()
    .AsQueryable()
    .Where(x => x.Id == id)
    .Include(x => x.Bars)
    .AsNoTracking()
    .ToListAsync();

foreach (var foo in foos)
{
    foreach (Bar bar in foo.Bars)
    {
        bar.Baz = null;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM