{"id":388891,"date":"2023-09-21T14:39:17","date_gmt":"2023-09-21T13:39:17","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=dotnet&#038;p=388891"},"modified":"2023-09-21T14:39:21","modified_gmt":"2023-09-21T13:39:21","slug":"eager-lazy-and-explicit-loading-with-entity-framework-core","status":"publish","type":"dotnet","link":"https:\/\/blog.jetbrains.com\/en\/dotnet\/2023\/09\/21\/eager-lazy-and-explicit-loading-with-entity-framework-core","title":{"rendered":"Eager, Lazy and Explicit Loading with Entity Framework Core"},"content":{"rendered":"\n<p>Entity Framework Core (EF Core) supports a number of ways to load related data. There\u2019s eager loading, lazy loading, and explicit loading. Each of these approaches have their own advantages and drawbacks. In this post, let\u2019s have a quick look at each of these ways to load data for navigational properties!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Eager loading<\/h2>\n\n\n\n<p>Eager loading is when you query one type of entity and immediately load related entities as part of it. For example, loading an <code>Invoice<\/code> and immediately bringing in a collection of <code>InvoiceLine<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"wpcustom\" data-enlighter-highlight=\"2\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var invoices = db.Invoices\n    .Include(invoice => invoice.InvoiceLines)\n    .ToList();\n\n\/\/ All invoices are already loaded...\nforeach (var invoice in invoices)\n{\n    \/\/ ...including all their Invoice lines\n    foreach (var invoiceLine in invoice.InvoiceLines)\n    {\n        \/\/ ...\n    }\n}<\/pre>\n\n\n\n<p>It\u2019s even possible to include navigational properties of <code>InvoiceLine<\/code>, for example including the referenced <code>Product<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"wpcustom\" data-enlighter-highlight=\"2-3\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var invoices = db.Invoices\n    .Include(invoice => invoice.InvoiceLines)\n    .ThenInclude(invoiceLine => invoiceLine.Product)\n    .ToList();\n\n\/\/ All invoices are already loaded...\nforeach (var invoice in invoices)\n{\n    \/\/ ...including all their invoice lines...\n    foreach (var invoiceLine in invoice.InvoiceLines)\n    {\n        \/\/ ...and related product!\n        var product = invoiceLine.Product;\n\n        \/\/ ...\n    }\n}<\/pre>\n\n\n\n<p>With eager loading, you can look at the <code>.Include()<\/code> (and <code>.ThenInclude()<\/code>) calls to see which navigational properties will be loaded from the database as part of the query. As a developer, I like how clearly this communicates what\u2019s going on in code.<\/p>\n\n\n\n<p>Eager loading does come with one issue: you have to remember to call <code>.Include()<\/code> to query related entities. If you forget to do this, your code will keep working, except for the fact that in our example no invoice lines may be loaded at all, or only a partial set is returned if some invoice lines were loaded before. As a result, your code will not be reliable.<\/p>\n\n\n\n<p><a href=\"https:\/\/www.jetbrains.com\/resharper\/\" target=\"_blank\" rel=\"noopener\">ReSharper<\/a> and <a href=\"https:\/\/www.jetbrains.com\/rider\/\" target=\"_blank\" rel=\"noopener\">JetBrains Rider<\/a> both have inspections to help you out in such cases. When accessing any navigational property that has not been loaded, the IDE will tell you that the <strong>Usage of navigational property can return incomplete data<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"716\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/09\/image-13.png\" alt=\"\" class=\"wp-image-388978\"\/><\/figure>\n\n\n\n<p>As usual, there\u2019s a quick-fix available through the <kbd>Alt+Enter<\/kbd> menu where the IDE can update the query for you.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1250\" height=\"90\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/09\/image-14.png\" alt=\"\" class=\"wp-image-388989\"\/><\/figure>\n\n\n\n<p>ReSharper and JetBrains Rider then automatically add the necessary <code>.Include()<\/code> calls for navigational properties, including those for navigational properties of navigational properties.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"226\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/09\/image-15.png\" alt=\"\" class=\"wp-image-389000\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Lazy loading<\/h2>\n\n\n\n<p>With lazy loading, navigational properties are queried automatically when needed. This way, you don\u2019t have to remember to add all of those <code>.Include()<\/code> calls like with eager loading! Additionally, only the data you are actually working with is queried from the database.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"wpcustom\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var invoices = db.Invoices\n    .ToList();\n\n\/\/ All invoices are already loaded...\nforeach (var invoice in invoices)\n{\n    \/\/ ...invoice lines are queried when accessed...\n    foreach (var invoiceLine in invoice.InvoiceLines)\n    {\n        \/\/ ...the related product is also queried when accessed\n        var product = invoiceLine.Product;\n\n        \/\/ ...\n    }\n}<\/pre>\n\n\n\n<p>Lazy loading does come at a cost: there\u2019s a big risk that for each <code>Invoice<\/code> entity you process (<strong>N<\/strong>), another query is run on the database to fetch the collection of <code>InvoiceLine<\/code> for that entity (<strong>+1<\/strong>). As a result, the database may have to process N+1 queries instead of just one query in the eager loading example we saw earlier.<\/p>\n\n\n\n<p>The N+1 problem becomes very visible when using a (database) profiler. Tools like <a href=\"https:\/\/blog.jetbrains.com\/en\/dotnet\/2023\/03\/02\/optimizing-entity-framework-core-database-queries-with-dynamic-program-analysis#beware-of-n+1-performance\">Dynamic Program Analysis can highlight the number of queries executed<\/a>. With lazy loading, you\u2019ll see many more queries being executed than you may have expected.<\/p>\n\n\n\n<p>If you do decide to use lazy loading in your application, a similar inspection is available in ReSharper and JetBrains Rider: <strong>Possible multiple queries to the database (N+1 problem)<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"1600\" height=\"390\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2023\/09\/image-16.png\" alt=\"\" class=\"wp-image-389011\"\/><\/figure>\n\n\n\n<p><kbd>Alt+Enter<\/kbd> provides a quick-fix that switches to eager loading for this particular query, again adding <code>.Include()<\/code> for navigational properties that are accessed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Explicit loading<\/h2>\n\n\n\n<p>Explicit loading is somewhat similar to lazy loading, in that data from navigational properties is only queried from the database when it\u2019s needed. The difference with lazy loading is that you\u2019ll have to query the related data explicitly. If you don\u2019t, only a partial or empty collection of invoice lines will be available, which would make your code unreliable.<\/p>\n\n\n\n<p>Therefore, you\u2019ll have to load the navigational data explicitly by asking the database context:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"wpcustom\" data-enlighter-highlight=\"8\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">var invoices = db.Invoices\n    .ToList();\n\n\/\/ All invoices are already loaded...\nforeach (var invoice in invoices)\n{\n    \/\/ ...but you'll have to explicitly load invoice lines when they are needed\n    db.Entry(invoice).Collection(p => p.InvoiceLines).Load();\n\n    foreach (var invoiceLine in invoice.InvoiceLines)\n    {\n        \/\/ ...\n    }\n}<\/pre>\n\n\n\n<p>With explicit loading, it becomes very clear where navigational data is queried in code. However, the N+1 issue is also present here, as each time an invoice is processed its invoice lines are queried separately.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>In this post, we have seen different mechanisms for loading related data with Entity Framework Core:<\/p>\n\n\n\n<ul>\n<li>Eager loading queries navigational data together with the main entity type, but you have to remember to call `.Include()` for each of them to do so.<\/li>\n\n\n\n<li>Lazy loading makes querying navigational data implicit, fetching data from the database when it\u2019s accessed. The downside is that this may result in running too many queries due to the N+1 problem.<\/li>\n\n\n\n<li>Explicit loading is similar, in that related data is only loaded when you need it. The downside is that you have to remember to load the data explicitly, and the N+1 problem is also very likely with this approach.<\/li>\n<\/ul>\n\n\n\n<p>Over the past years, I\u2019ve seen many projects where the N+1 problem was causing performance issues, I\u2019m happy the EF Core team did a great job steering developers towards eager loading, while <a href=\"https:\/\/learn.microsoft.com\/en-us\/ef\/core\/querying\/related-data\/lazy\" target=\"_blank\" rel=\"noopener\">making lazy loading inconvenient to enable<\/a> (also see <a href=\"https:\/\/blog.jetbrains.com\/en\/dotnet\/2021\/02\/24\/entity-framework-core-5-pitfalls-to-avoid-and-ideas-to-try#do-not-enable-lazy-loading\">Entity Framework Core \u2013 Pitfalls To Avoid<\/a>). It\u2019s definitely an option that may make sense for your scenario.<\/p>\n\n\n\n<p>Especially combined with tools like ReSharper and JetBrains Rider suggesting to add the necessary <code>.Include()<\/code> calls, eager loading is a great default approach for most codebases using EF Core.<\/p>\n\n\n\n<p><em>Image credit: <\/em><a href=\"https:\/\/unsplash.com\/@jacalynbeales?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\" target=\"_blank\" rel=\"noopener\"><em>Jacalyn Beales<\/em><\/a><em> on <\/em><a href=\"https:\/\/unsplash.com\/photos\/m1ihpPLnjdI?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText\" target=\"_blank\" rel=\"noopener\"><em>Unsplash<\/em><\/a><\/p>\n","protected":false},"author":118,"featured_media":388925,"comment_status":"closed","ping_status":"closed","template":"","categories":[4992,1401],"tags":[6927,756,46,1978],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/388891"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/types\/dotnet"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/users\/118"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/comments?post=388891"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/388891\/revisions"}],"predecessor-version":[{"id":389030,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/dotnet\/388891\/revisions\/389030"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media\/388925"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/media?parent=388891"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/categories?post=388891"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/tags?post=388891"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/en\/wp-json\/wp\/v2\/cross-post-tag?post=388891"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}