#SharePointProblems | Koskila.net

Solutions are worthless unless shared! Antti K. Koskela's Personal Professional Blog
>

EF Core returns null for a Navigation property

koskila
Reading Time 4 min
Word Count 704 words
Comments 45 comments
Rating 4.7 (13 votes)
View

This article describes how to fix a situation, where you can't use any of the navigation properties of your Entity Framework Core entities, since they are all null, despite data being in the database for sure.

So, another day, another error. This time I can't blame SharePoint, since I just messed up with ASP.NET Core and Entity Framework Core. :)

Symptoms

No matter what I'd do, I was getting null values for my navigation properties on my entities. They'd would always be null during runtime despite the columns having proper values in the database. Does this sound like utter and moonspeak? Well, easier to show the code!

So I had something like this in my Site.cs model:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace ...Models
{
    public class Site : BaseEntity
    {
        [Key]
        public int SiteId { get; set; }

        [Required]
        [MaxLength(500)]
        public string SiteUrl { get; set; }

        public string UserName { get; set; }

        [InverseProperty("PrimarySite")]
        public virtual ICollection<applicationuser> PrimaryUsers { get; set; }
    }
}

And this in my ApplicationUser class:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;

namespace ...Models
{
    public class ApplicationUser : IdentityUser
    {
        public string LastLoginProvider { get; set; }
        public DateTime? LastLoginDate { get; set; }
        public string UserAgent { get; set; }
        public string IpAddress { get; set; }
        public string FullName { get; set; }
        public DateTime? CreatedAt { get; set; }

        [ForeignKey("SiteId")]
        public virtual Site PrimarySite { get; set; }

    }
}

And in my controller, the navigation property for an instance of ApplicationUser would always be null, so the PrimarySite could not be accessed - even though I could verify from the database, that the foreign key is indeed populated!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;

namespace ...Controllers
{
    public class SitesController : Controller
    {
        private readonly ApplicationDbContext _db;
        private readonly SignInManager _signInManager;
        private readonly UserManager _userManager;
        private readonly ILogger _logger;

        public SitesController(UserManager userManager, SignInManager signInManager, ILogger logger, ApplicationDbContext db)
        {
            _signInManager = signInManager;
            _userManager = userManager;
            _logger = logger;
            _db = db;
        }

        [Authorize]
        [Route("[controller]/[action]")]
        public IActionResult UpdateSelection()
        {
            var user = _userManager.GetUserAsync(HttpContext.User).Result;

            var siteViewModel = new SiteViewModel();

            // The following line would always be null - no matter what data DB has
            if (user.PrimarySite != null) siteViewModel.ParseValues(user.PrimarySite);

            return View(siteViewModel);
        }

        // Rest of the implementation omitted

    }
}

Every time and for every entity, user.PrimarySite was always null. The following call, for example, would always throw an exception:

siteViewModel.ParseValues(user.PrimarySite);

This is frustrating - I tried quite a few different things, but couldn't get it to populate even by explicitly requesting the navigation property by running .Include(p => p.PrimarySite);

I was getting flabbergasted. Is this issue something that's broken in Entity Framework for .NET Core? This isn't how it works in Entity Framework with "classic" .NET Framework!

But wait: I have been struggling with lazy loading sometimes before. And that was actually the reason here as well!

Problem: Lazy Loading is messing with my Navigation properties

By default, EFCore loads Navigation properties lazily - which means, that unless they're explicitly requested or loaded beforehand, they won't be populated. But this time, even explicit loading (adding .Include(PropertyName) in the command) didn't help. What do?

Well, I guess my issue was that I didn't actually have lazy loading configured. So I was apparently using it without configuring it. Not sure how I ended up with this mess, but at this point it made sense to properly configure it. But how?

Solution

Time needed: 10 hours.

How to configure Lazy Loading on entity Framework Core?

  1. You need to add this NuGet package (should be enough for any ASP.NET Core 2+ project)

    Microsoft.EntityFrameworkCore.Proxies

    See below - I got the 2.1.1 version (since my application was that version as well).
    Microsoft.EntityFrameworkCore.Procies NuGet package.

  2. Make all of your navigation properties virtual

    Like shown below:
    Navigation properties need to always be virtual when using Lazy Loading, so that EF Core can overwrite then with proxies during runtime execution.

  3. Configure it in your Startup.cs's ConfigureServices(), while you're adding your DbContext for Entity Framework Core.

    Of course, replace the DefaultConnection with your connection string's name:

    services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

  4. Rebuild and run - you should be good!


And you should be good! Your navigation properties should be loaded during runtime.

Comments

Interactive comments not implemented yet. Showing legacy comments migrated from WordPress.
Alex
2019-11-17 17:28:10)
Thanks a lot, really helped your material that much! Thank you for your work!
2019-11-18 14:14:59
Hi Alex, Thanks, I'm happy to hear it helped! :)
Adnan
2019-12-02 17:27:30)
Thank you very much. Time saved. God bless you!
2019-12-05 23:14:18
Thanks for the feedback, Adnan - I'm happy to hear it was helpful!
Ravi Kurapati
2019-12-13 17:04:11)
Thanks a lot!
2019-12-13 17:46:50
My pleasure, Ravi!
agnes
2020-01-18 02:31:04)
Thank you so much! Your article saved me hours of frustration and disappointment.
2020-01-19 08:15:59
Happy to hear it was helpful, Agnes! ☺️
Arun
2020-01-23 14:24:40)
Hi, I am not able to use services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); this in startup.cs. Though I used the similar in DBContext.cs as : optionsBuilder.UseMySql(GetEntityConnectionString(), x => x.ServerVersion("8.0.16-mysql")); optionsBuilder.UseLazyLoadingProxies(true); How could I use this in startup.cs?
2020-01-23 22:47:51
Hi Arun, Thanks for your comment! What's the error you're getting? What you're doing should achieve the same thing. You could try adding this in Startup.cs - it shouldn't usually be required if you're configuring proxies in your context class, but perhaps it is for MySQL..? services.AddEntityFrameworkProxies(); Let me know how it goes!
joshua
2020-02-07 20:35:21)
Idk how I feel about this solution but it works and I cant explain how it worked before this. haha. Thank you.
2020-02-10 22:58:05
My feelings exactly with a lot of the workarounds I post. Happy to be of help, though! :)
aharvey
2020-02-22 18:09:55)
Holy shit, EF core really sucks in this sense. I'm also a use of EF6
2020-02-24 21:11:20
Well, it IS another thing to have to remember. Both .NET and EF Core have quite a few of these small things to configure, failing of which causes all kinds of ridiculous issues. A lot of tiny, tiny things to blog about... :)
Atanas
2020-02-25 11:57:06)
Thank you dude! It worked :)
2020-03-03 20:58:03
Awesome, happy to be of help!
2020-04-15 23:51:48)
It worked!!! Thanks
radotooo
2020-04-22 01:21:26)
Thank you!
2020-04-23 16:39:05
Happy to be of help!
2020-04-29 10:24:02)
Hey, Great article! I like it. According to me: If you define your navigation property virtual, Entity Framework will at runtime create a new class (dynamic proxy) derived from your class and uses it instead of your original class. This new dynamically created class contains logic to load the navigation property when accessed for the first time. This is referred to as “lazy loading”. It enables Entity Framework to avoid loading an entire tree of dependent objects which are not needed from the database.
2020-05-02 23:40:14
Hmm - are you saying, that just by adding keyword "virtual", EF Core starts lazy-loading the navigation property regardless of lazy loading being configured or not? 🤔
Dan
2020-05-24 20:08:22)
You saved me! So glad to have found your post! Thanks
2020-05-24 21:16:00
Happy to hear it was helpful, Dan!
2020-07-10 11:18:30)
If you define your navigation property virtual, Entity Framework will at run time create a new class (dynamic proxy) derived from your class and uses it instead of your original class. This new dynamically created class contains logic to load the navigation property when accessed for the first time. This is referred to as “lazy loading”. It enables Entity Framework to avoid loading an entire tree of dependent objects which are not needed from the database. Regards Akansha
Antti K. Koskela
2020-07-11 13:18:26
Thanks for your comment, Akansha! You actually submitted exactly the same comment (both being copy-pastes from the top response to a related question on Stack Overflow) a couple of months ago. Appreciate the input regardless!
Conax
2021-01-20 02:31:53)
But, how is that when I use .Include(PropertyName) it just works without the need of the proxy? In other words, why does Include(PropertyName) sometimes works but sometimes doesn't work?
2021-01-20 21:21:45
Hi Conax, That was pretty much my experience without configuring Lazy Loading - does this still occur to you if you do something along the lines of this in your ConfigureServices? services.AddDbContext(options => { options.UseLazyLoadingProxies(); });
Naod
2021-03-05 04:07:36)
Thank you much for the solution, it has helped me in a test project. But in the mean time I faced another issue. I am implementing an API with angular and the data from the API will not get displayed if it has a navigation property, chained with include. I can see it in Swagger and the browser console but not in the browser generated by angular if it has a navigation property. Any idea?
Antti K. Koskela
2021-03-09 22:34:39
Hi Naod, Thanks for your question, but unfortunately I've been ridiculously successful at avoiding Angular so far - so can't really help you there. 😬
dave
2021-04-30 20:54:33)
Thank you so much for your article. I spent several hours trying to figure this out
Antti K. Koskela
2021-05-06 14:14:57
Happy to be of help, dave!
Jamie Finch
2021-07-14 23:29:08)
Thank you sir, you have saved me a lot of trouble :)
Antti K. Koskela
2021-07-15 00:06:26
Hi Jamie, Thanks for the comment - feedback like this is my biggest motivator for taking the time to write these articles! :)
2021-09-05 18:15:54)
Thank you thank you thank you, I have been struggling with this for days
Antti K. Koskela
2021-09-09 14:31:59
Thanks for your comment Simphiwe - happy to hear it was helpful!
Leszek
2021-10-09 17:08:11)
Thank you a lot!
Antti K. Koskela
2021-10-11 11:41:17
My pleasure!
Santosh
2021-10-29 12:32:07)
I have .NET standard 2.0 project for DAL. So i used Nuget version 3.1.20.0 for Microsoft.EntityFrameworkCore.Proxies which is last supported version for .NET standard 2.0. optionsBuilder.UseLazyLoadingProxies().UseSqlServer... is added in OnConfiguring method of DBContext class. However i get following error. I cross verified that all of my EF Core nugets are in 3.1.20. Could you please advise what may be going wrong here? Method 'ProcessModelFinalized' in type 'Microsoft.EntityFrameworkCore.Proxies.Internal.ProxyBindingRewriter' from assembly 'Microsoft.EntityFrameworkCore.Proxies, Version=3.1.20.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' does not have an implementation.
Antti K. Koskela
2021-12-17 23:19:35
Hi Santosh, Apologies for being a bit late on this one - doesn't ring any bells, kinda sounds like you're missing a dependency for the package. Were you able to figure it out? Would love to know how!
Alex
2022-10-29 18:14:34)
Thanks a lot!
2022-10-31 13:59:16
Happy I could help, Alex :)