Miina (@minepupper) is fixing them bugs

EF Core returns null for a Navigation property

This post was most recently updated on February 8th, 2021.

3 min read.

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.

mm
5 12 votes
Article Rating
Subscribe
Notify of
guest

45 Comments
most voted
newest oldest
Inline Feedbacks
View all comments