简体   繁体   English

如何在实体框架 LINQ 中显式不加载嵌套类?

[英]How to explicitly not load nested classes in Entity Framework LINQ?

This is an ASP Core 3.1 project using Entity Framework.这是一个使用实体框架的 ASP Core 3.1 项目。 I tried to find an answer but Google kept pointing me to the [NotMapped] attribute which is not related to my question.我试图找到答案,但 Google 一直将我指向与我的问题无关的 [NotMapped] 属性。

I have 3 classes: Document, Paragraph, and Sentences A Document has many paragraphs, a paragraph has many sentences.我有 3 个类:文档、段落和句子 一个文档有很多段落,一个段落有很多句子。

public class Document {
   public List<Paragraphs> paras {get;set;}
}
public class Paragraph{
   public int ParagraphId {get;set; }
   public List<Sentences> sents {get;set;}
}
public class Sentence{
   public string sent {get;set;}
}

When I pull documents from SQL using EF Core, I would like to get all documents and their respective paragraphs, but I do NOT want to include each paragraphs sentences.当我使用 EF Core 从 SQL 中提取文档时,我想获取所有文档及其各自的段落,但我不想包含每个段落的句子。 So if I looked at a Paragraph after pulling it from SQL, its Sentence List should be empty (the reason for this is I am trying to avoid overhead from all these sentences for this particular situation).因此,如果我在从 SQL 拉出一个段落后查看它,它的句子列表应该是空的(这样做的原因是我试图避免在这种特殊情况下所有这些句子的开销)。

_context.Documents.Include(x=> x.Paragraphs).ToList();

Is there some kind of method that I could attach to Paragraphs within the Include lambda to not include the Sentences?是否有某种方法可以附加到包含 lambda 中的段落以不包含句子?

It's hard to tell for sure without seeing how you configured your Db Context, but the most likely reason why that's happening is, as @GertArnold pointed in the comments, that you configured the Sentences as a Collection of owned types .如果没有看到您如何配置 Db 上下文,很难确定,但最可能的原因是,正如@GertArnold 在评论中指出的那样,您将Sentences配置为拥有类型的集合

So you might have something similar to the following:所以你可能有类似以下的东西:

modelBuilder.Entity<Paragraph>().OwnsMany(p => p.Sentences, s =>
{
    s.WithOwner().HasForeignKey("ParagraphId");
    ...
});

If you have configured your entities that way, then the Sentences will always be included when querying the Paragraphs by default.如果您以这种方式配置了实体,则默认情况下查询Paragraphs时将始终包含Sentences

From the documentation :文档中:

When querying the owner the owned types will be included by default.查询所有者时,默认情况下将包含拥有的类型。 It is not necessary to use the Include method不必使用 Include 方法

To achieve the desired behaviour, you should instead configure your entities as a normal 1-N relationship:要实现所需的行为,您应该将实体配置为正常的 1-N 关系:

modelBuilder.Entity<Paragraph>()
        .HasMany(p => p.Sentence)
        .WithOne();

PD: you should name your entities in singular instead of plural. PD:你应该用单数而不是复数来命名你的实体。 I see that you have mixed up the names (ie Paragraphs =/= Paragraph).我看到您混淆了名称(即段落 =/= 段落)。

In entity framework always use Select to select the properties that you plan to use.在实体框架中,始终使用Select到 select 您计划使用的属性。 Only use Include if you plan to update the fetched values.仅当您计划更新获取的值时才使用Include

Certainly don't use it to safe you some typing!当然不要用它来保护你的打字安全!

The reason is that Include will fetch all columns of the table.原因是 Include 将获取表的所有列。 Many of them you probably won't use.其中许多您可能不会使用。

Suppose you have a database with Schools and Students , with a standard many-to-many relation between Schools and Students: every School has zero or more Students;假设您有一个包含 Schools 和 Students 的数据库,在SchoolsStudents之间具有标准的多对多关系:每个 School 都有零个或多个 Student; every Student attends exactly one School, namely the School that the foreign key refers to.每个Student就读一所学校,即外键所指的学校。

Every Student of School [10] will have a foreign key SchoolId with a value equal to 10. So if use Include to fetch School [10] with its 2000 Students , you will be transferring this value 10 over 2000 times, while you already know the value. School [10] 的每个 Student 将有一个外键SchoolId ,其值等于 10。因此,如果使用 Include 获取School [10] with its 2000 Students ,您将在 2000 次以上传输此值 10,而您已经知道价值。

ICollection vs List ICollection 与列表

In entity framework relations between tables are described as virtual properties.在实体框架中,表之间的关系被描述为虚拟属性。 If you follow the entity framework code first conventions , you'll see that the one-to-many relations are designed as virtual ICollection<...>如果您遵循实体框架代码优先约定,您将看到一对多关系被设计为virtual ICollection<...>

You decided to use List<Paragraph> .您决定使用List<Paragraph> Are you sure that document.Paras[4] is the fourth paragraph?您确定document.Paras[4]是第四段吗? Even if you inserted paragraph [2] after you created paragraph [4]?即使您在创建第 [4] 段之后插入了第 [2] 段?

Furthermore: you use a List<...> , not an IList<...> , thus forcing entity framework to copy it's fetched data to a List, while internally it might have had a much smarter solution to fetch the collections.此外:您使用List<...> ,而不是IList<...> ,从而强制实体框架将其获取的数据复制到 List 中,而在内部它可能有一个更智能的解决方案来获取 collections。

Consider to stick to the Conventions and use virtual ICollection<...> , instead of `List<...>.考虑遵守约定并使用virtual ICollection<...> ,而不是 `List<...>。

Use the Virtual ICollections使用虚拟 ICollections

Requirement : I would like to get all documents and their respective paragraphs, but I do NOT want to include each paragraphs sentences.要求:我想获取所有文件及其各自的段落,但我不想包含每个段落的句子。

var documentsWithTheirParagraphs = dbContext.Documents.Select(document => new
{
    // Select only the document properties that you plan to use:
    Id = document.Id,
    Title = document.Title,
    ...

    Paragraphs = documents.Paragraphs.Select(paragraph => new
    {
         // again: only the properties that you plan to use!
         Id = paragraph.Id,
         Title = paragraph.Title,
         ...

         // foreign key not needed, you already know the value
         // DocumentId = paragraph.DocumentId,

         // not wanted in your requirement, added to show how easy it is
         // if you use the virtual ICollections
         Sentences = paragraph.Sentences.Select(sentence => new
         {
             Id = sentence.Id,
             Text = sentence.Text,
             ...
         })
         .ToList(),
    })
    .ToList(),
});

Using the virtual ICollection<...> is a very intuitive method.使用virtual ICollection<...>是一种非常直观的方法。 Entity framework knows the relations between the tables, and creates the proper (Group)Join.实体框架知道表之间的关系,并创建适当的 (Group)Join。

Do the GroupJoin yourself自己做组加入

Some people mention that the virtual ICollections can't be used when using EF-core.有人提到使用 EF-core 时不能使用虚拟 ICollections。 If you have an entity framework version that doesn't support this, you'll have to do the GroupJoin yourself.如果您的实体框架版本不支持此功能,则必须自己进行 GroupJoin。 If there are not too many tables involved, the GroupJoin is not too complicated:如果涉及的表不是太多,GroupJoin 也不会太复杂:

var documentsWithTheirParagraphs = dbContext.Documents.GroupJoin(
    dbContext.Paragraphs,

    document => document.Id,              // from each Document take the primary key
    paragraph => paragraph.DocumentId,    // from each Paragraph take the foreign key

    (document, paragraphsOfThisDocument) => new   // when they match, make one new:
    {
        Id = document.Id,
        Title = document.Title,
        ...

        Paragraphs = paragraphsOfThisDocument.Select(paragraph => new
        {
             Id = paragraph.Id,
             Title = paragraph.Title,
             ...
         })
         .ToList(),
    });

Entity Framework will fetch all nested classes from the database IF the Lazy Loading option is enabled and WHEN the nested classes are first requested - ie when the code execution reaches a point where it says paragraph.sents .如果启用了延迟加载选项并且首次请求嵌套类时,实体框架将从数据库中获取所有嵌套类 - 即,代码执行到达它所说的paragraph.sents点时。 If you never request the sentences in your code, they will never be fetched from the database.如果您从不请求代码中的句子,则永远不会从数据库中获取它们。

But to get your head clear you can disable Lazy Loading and then only the nested classes that you include in your queries (with the include method) will be fetched.但是为了让你的头脑清晰,你可以禁用延迟加载,然后只获取你在查询中包含的嵌套类(使用 include 方法)。

Please note that Lazy Loading is disabled by default in .net core, so you must have enabled it somewhere (your startup class or context class).请注意,延迟加载在 .net 内核中默认禁用,因此您必须在某处启用它(您的启动 class 或上下文类)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM