Search code examples
c#asp.netasp.net-identityrazor-pages

RazorPages relationship on Identity not returning related data


I have created a Razor Pages project and I have created a data model called company and modified the User Identity to have a relationship to company.

public class ApplicationUser : IdentityUser
{
    public string FirstName { get; set; }
    public string Surname { get; set; }
    public Company Company { get; set; }
}

public class Company
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string AccessCode { get; set; }
    public ICollection<ApplicationUser> Employees { get; set; }
}

Using the below code I am able to create a Company and assign that Company to the user

Company c = new Company { Name = Input.Name };
_context.Companies.Add(c);            
await _context.SaveChangesAsync();

ApplicationUser u = await _userManager.GetUserAsync(User);
u.Company = c;
await _context.SaveChangesAsync();

Screenshot of Companies

Screenshot of User

So I am able to create a company and link the Company to the User however when I try and check if the current user has a Company associated with it using the below code it fails to get the Company, x.Company returns Null even though there is a company linked to the current user

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Fleet_Man.Data;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Fleet_Man.Pages
{
    public class CompanyModel : PageModel
    {
        private readonly UserManager<ApplicationUser> _userManager;
        ApplicationDbContext _context;

        public CompanyModel(UserManager<ApplicationUser> userManager, ApplicationDbContext appDbContext)
        {
            _userManager = userManager;
            _context = appDbContext;
        }
        public Boolean CompanyMember { get; set; }
        [BindProperty]
        public InputModel Input { get; set; }

        public string ReturnUrl { get; set; }

        public string Message { get; set; }


        public class InputModel
        {
            [Required]
            [Display(Name = "Company Name")]
            public string Name { get; set; }
        }


        public async Task<IActionResult> OnGetAsync(string returnUrl = null)
        {
            //ApplicationUser x = await _userManager.GetUserAsync(User);
            //Updated to reflect suggestion below
            ApplicationUser x = await _userManager.GetUserAsync(User).Inlcude(u => u.Company).FirstOrDefaultAsync(u => u.FirstName == User);
            ReturnUrl = returnUrl;

            if (x.Company == null)
            {
                CompanyMember = false;
                Message = "You don't belong to a company.";
            }
            else
            {
                CompanyMember = true;
                Message = "You belong to a company.";
            }

            return Page();
        }
        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            if (ModelState.IsValid)
            {
                ReturnUrl = returnUrl;
                Company c = new Company { Name = Input.Name };
                _context.Companies.Add(c);


                await _context.SaveChangesAsync();
                ApplicationUser u = await _userManager.GetUserAsync(User);
                u.Company = c;
                await _context.SaveChangesAsync();
                //var p = new Patient { Title = Input.Title, FirstName = Input.FirstName, Surname = Input.Surname, DOB = Input.DOB, NHSNumber = Input.NHSNumber };
                //_context.Patients.Add(p);
                //await _context.SaveChangesAsync();
                //Message = "Patient (" + Input.FirstName + " " + Input.Surname + ") created successfully";

                //return RedirectToPage("AllPatients");
            }
            return Page();


        }
    }
}

Solution

  • Linked entities are not returned automatically. You need to use one of the strategy described here: Loading Related Data

    I don't have implementation details of _userManager but I assume you need to change it something like this

    public async ApplicationUser GetUserAsync(string User) =>
      await _context.Users
                    .Inlcude(u => u.Company) // explicitly load Company
                    .FirstOrDefaultAsync(u => u.FirstName == User)
    

    If you use .net core UserManager then you can't modify its code, but you can still load referenced entity as described in the link I posted above:

    var x = await _userManager.GetUserAsync(User)
    _context.Entry(x)
            .Reference(b => b.Company)
            .Load();