I am using asp.net core identity, but have deliberately separated the user specific information into another table which is linked via the application user class.
How would I access this information from the login partial view?
Other answers only define so far as to pull the information from the application user class. ie:
var personID = UserManager.GetUserAsync(User).Result.PersonID;
Where as I need something like:
var fullName = UserManager.GetUserAsync(User).Result.Person.FullName;
I'm guessing the code behind doesn't load the child table, as I get the the following error on the Person object:
'Object reference not set to an instance of an object.'
The rest of my code is below for reference.
Application User:
public class ApplicationUser : IdentityUser
{
[ForeignKey("Person")]
public int PersonID { get; set; }
public virtual Person Person { get; set; }
}
Person Class
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName ", " FirstName; }
}
public string Address { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
Login Partial:
@using Microsoft.AspNetCore.Identity
@using Models;
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
<ul >
@if (SignInManager.IsSignedIn(User))
{
<li >
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity?.Name!</a>
@{
var personResult = UserManager.GetUserAsync(User).Result;
var fullName = personResult.Person.FullName;
}
</li>
<li >
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/", new { area = "" })" method="post" >
<button type="submit" >Logout</button>
</form>
</li>
}
else
{
<li >
<a asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li >
<a asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
CodePudding user response:
_userManager.GetUserAsync(User)
is based on EF Core, it won't load related data by defalut,you could try with lazyloading as the
Models:
public class ApplicationUser : IdentityUser
{
//you have to set the property virtual here
public virtual Person Person { get; set; }
}
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[NotMapped]
[Display(Name = "Full Name")]
public string FullName
{
get { return LastName ", " FirstName; }
}
public string Address { get; set; }
//you have to set the property virtual here
public virtual ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
}
modified the codes which regist the dbcontext:
builder.Services.AddDbContext<IdentityLazyloadingContext>(options =>
{
options.UseSqlServer(connectionString);
options.UseLazyLoadingProxies();
});
The result:
CodePudding user response:
Answer from Ruikai Feng marked as answer as it set me on the right track. However I ended up using lazy loading without the proxies framework as follows:
public class ApplicationUser : IdentityUser
{
private Person _Person;
public ApplicationUser()
{
}
private ApplicationUser(Action<object, string> lazyLoader)
{
LazyLoader = lazyLoader;
}
private Action<object, string> LazyLoader { get; set; }
[ForeignKey("Person")]
public int PersonID { get; set; }
public Person Person
{
get => LazyLoader.Load(this, ref _Person);
set => _Person = value;
}
}
Extension:
public static class PocoLoadingExtensions
{
// Load function for Lazy Loading, used in Application User.
public static TRelated Load<TRelated>(
this Action<object, string> loader,
object entity,
ref TRelated navigationField,
[CallerMemberName] string navigationName = null)
where TRelated : class
{
loader?.Invoke(entity, navigationName);
return navigationField;
}
}