Search code examples
c#entity-frameworklinqasp.net-coreasp.net-identity

How can I load this City property in my Address entity when using Entity Framework Core?


I have an application in ASP.NET 6 that uses Entity Framework core as ORM. Just as a side-note I don't have a lot of experience with ORMs.

I have the following entities:

ApplicationUser:

public class ApplicationUser : IdentityUser
{
    public string FullName { get; set; } = string.Empty;
    public bool IsProfessional { get; set; } = false;
    public ICollection<Address>? Addresses { get; set; } = default;
}

A user can have several addresses, hence the ICollection property.

Address:

public class Address
{
    public string Id { get; set; } = string.Empty;
    public string UserId { get; set; } = string.Empty;
    public ApplicationUser User { get; set; } = default;
    public string Street { get; set; } = string.Empty;
    public string Number { get; set; } = string.Empty;
    public string Neighborhood { get; set; } = string.Empty;
    public string ZipCode { get; set; } = string.Empty;
    public string CityId { get; set; } = string.Empty;
    public City City { get; set; } = default;
}

The Address user contains the UserId of the user, and a User property for navigation purposes. It also contains a CityId, and a City property also for navigation purposes.

City:

public class City
{
    public string Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public State State { get; set; } = default;
    public ICollection<Address>? Addresses { get; set; } = default;
}

A city belogs to several addresses, so here I have the collection Addresses for navigation purposes.

I'm using Identity Framework to manage my users. And at some point within my code I need to load the user (using the UserManager) and the user related properties, such as address, and related to address, the city.

Here's the snippet where I'm having issues:

public async Task<GetUserQueryResponse> Handle(GetUserQuery request, CancellationToken cancellationToken)
    {

        var user = await _userManager.Users
            // Here I include the Addresss and hope that the City property is loaded
            // for each address
            .Include(u => u.Addresses) 
            .Where(u => u.Id == request.UserId).FirstOrDefaultAsync();

        var userAddresses = new List<GetAddressForUserDTO>();
        foreach (var address in user.Addresses)
        {
            userAddresses.Add(new GetAddressForUserDTO()
            {
                Id = address.Id,
                UserId = address.Id,
                Street = address.Street,
                Number = address.Number,
                Neighborhood = address.Neighborhood,
                ZipCode = address.ZipCode,
                City = address.City.Name, // PROBLEM: The city is null, was not loaded.
                State = address.City.State.Abbreviation // PROBLEM: The city is null.
            });
        }

        // rest of the code
    }

Here's the output when debugging:

Sample

Is there a way I can write this query or change the entities to allow the framework to load the related properties when writing queries such as this one? What am I doing wrong?


Solution

  • You need to also include the City navigation property:

    var user = await _userManager.Users
                                 .Include(u => u.Addresses) 
                                 // loading the address, also get the City
                                 .ThenInclude(a => a.City)
                                 .Where(u => u.Id == request.UserId)
                                 .FirstOrDefaultAsync();