Always load entity for ApplicationUser (.NET MVC, Identity) - asp.net-mvc

I am having problems loading a entity that I have assigned to the ApplicationUser in my .NET core MVC application.
I have added one of my entities to the user class, see code below:
public class ApplicationUser : IdentityUser
{
public int? AzureBlobResourceId { get; set; }
[ForeignKey("AzureBlobResourceId")]
public AzureBlobResource AzureBlobResource { get; set; }
}
Ideally I want the AzureBlobResource object to be loaded when retrieving the user from the UserManager
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
Unfortunately though the AzureBlobResource object always is null, even when the AzureBlobResourceId has a value.
What am I missing here?
Thanks, Nikolai

You need to implement your userstore
public class ApplicationUser : IdentityUser {
public int? AzureBlobResourceId { get; set; }
[ForeignKey("AzureBlobResourceId")]
public AzureBlobResource AzureBlobResource { get; set; }
}
public class MyAppUserStore : UserStore<ApplicationUser>
{
public MyAppUserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
public override async Task<ApplicationUser> FindByIdAsync(string userId, CancellationToken cancellationToken = new CancellationToken())
{
return await Context.Set<ApplicationUser>().Include(p => p.AzureBlobResource).FirstOrDefaultAsync(u => u.Id == userId, cancellationToken: cancellationToken);
}
}
And in Sturtup.cs add
ervices.AddIdentity<ApplicationUser, IdentityRole>()
.AddUserStore<MyAppUserStore >()
.AddUserManager<UserManager<ApplicationUser>>()
.AddDefaultTokenProviders();

Related

StudentDbContext is null using Asp.net Core Web api

am a beginner in ASP.NET Core. I am creating a Web API service. While I am fetching the data from the database, I had a problem. What is the error I got? I have successfully done the database migration part and created the database successfully.
StudentDbContext is null
StudentController
namespace webb.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private StudentDbContext studentDbContext;
public StudentController(StudentDbContext studentDbContext)
{
studentDbContext = studentDbContext;
}
// GET: api/<EmployeeController>
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return studentDbContext.Student;
}
}
}
Model
public class Student
{
public int id { get; set; }
public string stname { get; set; }
public string course { get; set; }
}
}
StudentDbContext
public class StudentDbContext : DbContext
{
public StudentDbContext(DbContextOptions<StudentDbContext> options) : base(options)
{
}
public DbSet<Student> Student { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Data Source=.;Initial Catalog=ams;Integrated Security=True; TrustServerCertificate = True");
}
}
IDataService
public interface IDataService<T>
{
Task<IEnumerable<T>> GetAll();
Task<T> Get(int id);
Task<T> Create(T entity);
Task<bool> Delete(T entity);
Task<T> Update(T entity);
}
}
I have successfully done the database migration part and created the
database successfully. StudentDbContext is null
Well, two mistake has been done. Your model has no primary key. So you will always get null data when there is no primary key set to your table column.
Therefore, your model should be as following:
Model:
public class Student
{
[Key]
public int id { get; set; }
public string stname { get; set; }
public string course { get; set; }
}
Controller:
Another misake is here studentDbContext.Student; this will not bring anything. You would be liking to fetch student list instead. So you should write studentDbContext.Student.ToList();. As following"
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return studentDbContext.Student.ToList();
}
Note: In addition, your constructor convension is not correct, it can be written as following:
[Route("api/[controller]")]
[ApiController]
public class StudentController : ControllerBase
{
private readonly StudentDbContext _studentDbContext;
public StudentController(ApplicationDbContext studentDbContext)
{
_studentDbContext = studentDbContext;
}
// GET: api/<EmployeeController>
[HttpGet]
public IEnumerable<Student> Get()
{
// var studens = studentDbContext.Student;
return _studentDbContext.Student.ToList();
}
}
Note: You can check more details on asp.net core web api official document here
Output:
For further details you can have a look on official document here.

Complex custom user management identity asp.net core 3.1 running with breakpoints, and not running without breakpoints

I'm working on a new Visual Studio 2019 webapp project with asp.net core MVC 3.1, razor pages and scaffolded Identity.
I'm using a custom IdentityUser class
public class VivaceApplicationUser : IdentityUser
{
[PersonalData]
public string FirstName { get; set; }
[PersonalData]
public string LastName { get; set; }
//[DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
[DataType(DataType.Date)]
[PersonalData]
public DateTime DateOfBirth { get; set; }
[PersonalData]
public Address Address { get; set; }
[PersonalData]
public string AdditionaInfos { get; set; }
[PersonalData]
public LoginUserStatus Status { get; set; }
[PersonalData]
public bool hasPaidQuote { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime paidOnDate { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime paidValidity { get; set; }
[DataType(DataType.Date)]
[PersonalData]
public DateTime registeredSince { get; set; }
}
public enum LoginUserStatus
{
Submitted,
Approved,
Rejected
}
This class use an Address class defined as follows:
public class Address
{
public int AddressID { get; set; }
public string City { get; set; }
public int PostalCode { get; set; }
public string Street { get; set; }
public string CivicNumber { get; set; }
}
My ApplicationDbContext class looks like that:
public class ApplicationDbContext : IdentityDbContext<VivaceApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Address> Addresses { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
I can login in my webapp and create a new user.
In the database I can see the new user and his address as well (in the address table).
In the dbo.AspNetUsers I can also see the right AdressID.
My issue is now in deleting a user in the DeletePersonalDataModel class.
public class DeletePersonalDataModel : PageModel
{
private readonly UserManager<VivaceApplicationUser> _userManager;
private readonly SignInManager<VivaceApplicationUser> _signInManager;
private readonly ILogger<DeletePersonalDataModel> _logger;
private readonly ApplicationDbContext _context;
public DeletePersonalDataModel(
UserManager<VivaceApplicationUser> userManager,
SignInManager<VivaceApplicationUser> signInManager,
ILogger<DeletePersonalDataModel> logger,
ApplicationDbContext context)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_context = context;
}
[BindProperty]
public InputModel Input { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
public Address Address { get; set; }
}
public bool RequirePassword { get; set; }
public async Task<IActionResult> OnGet()
{
// Get user
var user = await _userManager.GetUserAsync(User);
// Get user from _context
var userFromContext = await _context.Users.FindAsync(user.Id);
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (addressFromContext == null)
{
return NotFound($"Unable to load user address of user with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
// Get user
var user = await _userManager.GetUserAsync(User);
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
if (user == null)
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (addressFromContext == null)
{
return NotFound($"Unable to load user address with ID '{_userManager.GetUserId(User)}'.");
}
RequirePassword = await _userManager.HasPasswordAsync(user);
if (RequirePassword)
{
if (!await _userManager.CheckPasswordAsync(user, Input.Password))
{
ModelState.AddModelError(string.Empty, "Incorrect password.");
return Page();
}
}
var result = await _userManager.DeleteAsync(user);
var resultRemoveAddress = _context.Addresses.Remove(addressFromContext);
var userId = await _userManager.GetUserIdAsync(user);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred deleting user with ID '{userId}'.");
}
//if (!resultRemoveAddress.)
//{
// throw new InvalidOperationException($"Unexpected error occurred deleting user address of user with ID '{userId}'.");
//}
await _context.SaveChangesAsync();
await _signInManager.SignOutAsync();
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
return Redirect("~/");
}
}
I can get the current (logged) user from the _userManager class in the OnGet() method
// Get user
var user = await _userManager.GetUserAsync(User);
I can also get the current (logged) user from the _contect class in the OnGet() method
// Get user from _context
var userFromContext = await _context.Users.FindAsync(user.Id);
And I can get the user's address from the _context class as well in the OnGet() method
// Get user address from _context
var addressFromContext = await _context.Addresses.FindAsync(user.Address.AddressID);
Setting a breakpoint on
// Get user
var user = await _userManager.GetUserAsync(User);
in the OnGet() method and waiting a couple of seconds I see this
But if I run without breakpoints I get an unhandled exception
What I'm doing wrong? And how to solve this issue?
If I run with breakpoints the database gets updated correctly. Both the user and his address get deleted.
Thanks very much!

How to force Asp.Net Identity 2.0 to use custom Roles rather the IdentityUserRole

I have extended the IdentityUserRole which is as follow
public class ApplicationUserRoles : IdentityUserRole<string>
{
[Key]
public string ApplicationId { get; set; }
public virtual AspNetApplications AspNetApplications { get; set; }
}
and my AspNetApplications Class is as follow
public class AspNetApplications
{
[Key]
public string ApplicationId { get; set; }
public string ApplicationName { get; set; }
}
Migration has created the AspNetApplications and ApplicationUserRoles tables in DB. A screen shot is as follow.
Following is my Identity Model
public class ApplicationUser : IdentityUser
{
public virtual AspNetApplications AspNetApplication { get; set; }
public virtual ApplicationUserRoles AspNetUserRoles { get; set; }
//public virtual ICollection<AspNetApplicationUsers> AspNetApplicationUsers { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public DbSet<AspNetApplications> AspNetApplications { get; set; }
public DbSet<ApplicationUserRoles> AspNetUserRoles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUserRoles>().HasKey(m => new { m.ApplicationId, m.UserId, m.RoleId });
}
}
everything is good so far but now when I inspect the User in my Account Controller, it is still bringing Role based information from AspNetUserRoles. Could please anyone tell me how I can make use of my custom ApplicationUserRoles instead of IdentityUserRole.
If you look at the signature of IdentityUser (your ApplicationUser inherits from it)
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
where TLogin : Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<TKey>
where TRole : Microsoft.AspNet.Identity.EntityFramework.IdentityUserRole<TKey>
where TClaim : Microsoft.AspNet.Identity.EntityFramework.IdentityUserClaim<TKey>
{
}
you can see that it accepts your custom definition of IdentityUserRole.
Your class MyApplicationUser you have to implement must implement the right type:
public class MyApplicationUser : IdentityUser<string, MyUserLogin, ApplicationUserRoles, MyUserClaim>
{
}
and your role:
public class MyRole : IdentityRole<string, ApplicationUserRoles>
{
}
and your ApplicationDbContext:
public class MyContext : IdentityDbContext<MyUser, MyRole, string, MyUserLogin, ApplicationUserRoles, MyUserClaim>
{
}
and your UserStore:
public class MyUserStore: UserStore<MyUser, MyRole, string, MyUserLogin, ApplicationUserRoles, MyUserClaim>
{
public MyUserStore(MyContext context)
: base(context)
{
}
}
That should be it, I guess. There's a github repo where I've played a bit with custom class and custom tables names.

UserManager Argument Type not within bounds

I'm rewriting an MVC3 application using MVC5 with EF6 and attempting to also migrate the membership and roles API to Identity 2. I've followed several guides but am now receiving build errors with which I need assistance.
My AccountController in part is as follows:
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountController(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
My UserManager is as follows:
public class UserManager : UserManager<User>
{
public UserManager()
: base(new UserStore<User>(new ApplicationDbContext()))
{
this.PasswordHasher = new SQLPasswordHasher();
}
}
My ApplicationDbContext is as follows:
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
this.Database.Log = Logger;
}
private void Logger(string log)
{
Debug.WriteLine(log);
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var application = modelBuilder.Entity<Application>();
application.HasKey(t => t.ApplicationId).ToTable("Applications");
modelBuilder.Entity<UserDbProfile>().ToTable("Profiles");
}
public virtual IDbSet<Application> Applications { get; set; }
public virtual IDbSet<UserDbProfile> Profiles { get; set; }
}
My User model is as follows:
public class User : IdentityUser
{
public User()
{
CreateDate = DateTime.UtcNow;
IsApproved = false;
LastLoginDate = DateTime.UtcNow;
LastActivityDate = DateTime.UtcNow;
LastPasswordChangedDate = DateTime.UtcNow;
LastLockoutDate = MinSqlDate;
FailedPasswordAnswerAttemptWindowStart = MinSqlDate;
FailedPasswordAttemptWindowStart = MinSqlDate;
Profile = new ProfileInfo();
}
public System.Guid ApplicationId { get; set; }
public bool IsAnonymous { get; set; }
public System.DateTime? LastActivityDate { get; set; }
public string Email { get; set; }
public string PasswordQuestion { get; set; }
public string PasswordAnswer { get; set; }
public bool IsApproved { get; set; }
public bool IsLockedOut { get; set; }
public System.DateTime? CreateDate { get; set; }
public System.DateTime? LastLoginDate { get; set; }
public System.DateTime? LastPasswordChangedDate { get; set; }
public System.DateTime? LastLockoutDate { get; set; }
public int FailedPasswordAttemptCount { get; set; }
public System.DateTime? FailedPasswordAttemptWindowStart { get; set;}
public int FailedPasswordAnswerAttemptCount { get; set; }
public System.DateTime? FailedPasswordAnswerAttemptWindowStart
{ get; set; }
public string Comment { get; set; }
public ProfileInfo Profile { get; set; }
private static readonly DateTime MinSqlDate =
DateTime.Parse("1/1/1754");
}
The specific errors received are similar to:
Error 16
Inconsistent accessibility: parameter type
'Microsoft.AspNet.Identity.UserManager' is less accessible than method
'Controllers.AccountController.AccountController(Microsoft.AspNet.
Identity.UserManager<Controllers.ApplicationUser>)'
Note that I've already created new tables in the database that migrated the old membership and roles for use with Identity 2.
What must be done to resolve the errors and ensure that the new Identity 2 methods are working correctly?
Update
My AccountController code is now as follows:
public AccountController()
: this(new UserManager(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
UserManager is as follows, in part:
public class UserManager : UserManager<User>
{
private UserStore<Controllers.ApplicationUser> userStore;
public UserManager()
: base(new UserStore<User>(new ApplicationDbContext()))
{
this.PasswordHasher = new SQLPasswordHasher();
}
}
Build error states:
Error 19 'UserManager' does not contain a constructor that takes 1 arguments
Note that the ApplicationUser controller was created but doesn't have any methods implemented. Is this controller, needed? Or, can I remove it and references to it?
Your AccountController needs to take your UserManager, not UserManager<ApplicationUser> which is part of the framework:
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountController(UserManager userManager)
{
UserManager = userManager;
}
public UserManager UserManager { get; private set; }
Also I noticed that in controller you have ApplicationUser, but your user object is actually User. Make sure you are consistent with your classes.

Why is my Extended Identity Account failing to automigrate to the AspNetUsers table?

I added a new property called ResetPassword to the "out of the box" ApplicationUser class but when I run "add-migration" the up/down methods in the auto generated migration class are blank and when I try to log on to the application the following error is raised "The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database".
This is my code:
public class ApplicationUser : IdentityUser
{
public bool ResetPassword { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
May DAL class looks like this:
public class DefaultConnection : DbContext
{
public DefaultConnection()
: base("DefaultConnection")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Category> Categories { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<Model> Models { get; set; }
public DbSet<Variable> Variables { get; set; }
public DbSet<Column> Columns { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<Config> Configs { get; set; }
public DbSet<Target> Targets { get; set; }
public DbSet<Organisation> Organisations { get; set; }
public DbSet<OrgGroup> OrgGroups { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
When logging on this is the line which throws the error:
var userManager = context.OwinContext.GetUserManager();
You have 2 contexts in your application. When you run migrations, you need to specify type of context you want migration for:
Add-Migration -configuration <Namespace>.ApplicationDbContext <Migrations-Name>
See this page for more details about using migrations with multiple DbContexts in the same assembly.

Resources