Extended IdentityUser not saving - asp.net

Allright! So I extended my IdentityUser but it is not saving my Users anymore. The code samples can be viewed below. I am trying to seed my database with an admin user but it is not being stored. I have tried running a debugger on my seeds, but it doesn't trigger on anything. So I'm kinda lost. So, here is the code.
My extended User Class:
public class User : IdentityUser, IBaseEntity
{
public virtual ICollection<TimeLogEntry> TimeLogEntries { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public bool IsRemoved { get; set; }
public virtual User CreatedBy { get; set; }
public virtual User UpdatedBy { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> 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;
}
}
Then ofcourse the Context that inherits from the IdentityDbContext:
public class Context : IdentityDbContext<User>, IContext
{
public Context()
: base("MyConnection", throwIfV1Schema: false)
{
Configuration.ProxyCreationEnabled = false;
}
// DBSETS
public static Context Create() => new Context();
// MODELBUILDER
}
and finaly my seed:
protected override void Seed(Context context)
{
// Launch debugger on seeds
//if (System.Diagnostics.Debugger.IsAttached == false)
// System.Diagnostics.Debugger.Launch();
SeedUsers(context);;
}
private void SeedUsers(Context context)
{
var manager = new UserManager<User>(new UserStore<User>(context));
var adminUser = new User
{
UserName = "Admin",
Email = "admin#test.com"
};
if (!manager.Users.Any())
{
manager.Create(adminUser, "Admin");
}
}
I hope someone can help me with this!

The Seed() that is inside your initializer will only run when your database is recreated - for instance when you are using DropCreateDatabaseIfModelChanges.
If you use migrations (MigrateDatabaseToLatestVersion initializer), there is another Seed() that runs every time you apply the migration using update-database.
http://blog.oneunicorn.com/2013/05/28/database-initializer-and-migrations-seed-methods/

Related

Limit fields available for editing using Razor pages and Entity framework CORE with ViewModel

User class
public class MyUser {
public string Forename { get; set; }
public string Surname { get; set; }
public string PWD { get; set; }
}
Password edit page
public class EditPasswordModel : PageModel {
[BindProperty]
public UserMain objRecord { get; set; }
public async Task<IActionResult> OnGetAsync() {
objRecord = await _context.MyUser
.SingleOrDefaultAsync(m => m.UserID == 666);
return Page();
}
public async Task<IActionResult> OnPostAsync() {
_context.Attach(objRecord).State = EntityState.Modified;
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
In this password changing page, I only want to update the PWD field.
The option to reload the record from the table in OnPostAsync() then copy everything over into objRecord except for PWD works, but feels wrong and wasteful.
I've used ViewModels to extend what's brought into a View, combining classes to be used in the View for example, but is there a way to use a ViewModel to limit which fields are available?

Logged user based connection string in .NET Core

I have an application that uses identity database to store users and customers.
Each customer has also a separate database with its data and its connection string is stored in Customer table in the identity database.
AspNetUsers has a field to tell which customer the user belongs to (also identity db).
I want to assign connection string to the user when he logs in and make it available in the application for the duration of the session.
I currently have customer model:
public partial class `Customer`
{
public int Id { get; set; }
public string Name { get; set; }
public int NoLicenses { get; set; }
public bool? Enabled { get; set; }
public string CustomerConnectionString { get; set; }
}
and user model:
public class ApplicationUser : IdentityUser
{
public string CustomerId { get; set; }
public bool? IsEnabled { get; set; }
// there ideally I'd have a connstring property
}
The models map db table fields.
I'm using .NET Core 1.1 and EF Core.
With the defalut ASP.NET Identity template , you can :
extend the ApplicationUser class in Models folder :
public class ApplicationUser : IdentityUser
{
public string CustomerId { get; set; }
public bool? IsEnabled { get; set; }
//add your custom claims
public string CustomerConnectionString { get; set; }
}
Add your custom model to ApplicationDbContext in Data folder :
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
public DbSet<Customer> Customers { get; set; }
}
Sync your database : Add-Migration xxxx , then run the Update-Database command in Package Manager Console . Now you have the Customer table and have CustomerConnectionString column in AspNetUsers table.
Create you own implementation of IUserClaimsPrincipalFactory by inheriting the default one to generate a ClaimsPrincipal from your user :
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>
{
public AppClaimsPrincipalFactory(
UserManager<ApplicationUser> userManager
, RoleManager<IdentityRole> roleManager
, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{ }
public async override Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
var principal = await base.CreateAsync(user);
if (!string.IsNullOrWhiteSpace(user.CustomerId))
{
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("customid", user.CustomerId)
});
}
if (!string.IsNullOrWhiteSpace(user.CustomerConnectionString))
{
((ClaimsIdentity)principal.Identity).AddClaims(new[] {
new Claim("CustomerConnectionString", user.CustomerConnectionString)
});
}
return principal;
}
}
Register the custom factory you just created in your application startup class, after adding Identity service:
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddScoped<Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
Then you could access the claims like :
var connectString = User.Claims.FirstOrDefault(c => c.Type == "CustomerConnectionString").Value;
Modify the creating/editing user view/controller , add the customer dropdownlist on view , get the custom id in Register function in AccountController , query the connectingString of custom from db , and save in ApplicationUser object :
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);

EF update ApplicationUser(inherit from IdentityUser) with many to many relationship collections

Customize Identity user object contains list object region as with code. When user is registering he can request what are the regions he can work on and what is his role. Once user register email message goes to admin to review user and approve registration request. Until admin approved user is lock. Amin can modify the user region selections and request role if necessary. So Problem come in here. How could I update application user and his regions and role? I tried below but it gave me an exception. Do I need to first update application user then retrieve it and add regions and roles make a send update?( many DB calls)
The property 'Regions' on type 'ApplicationUser' is not a primitive or complex property. The Property method can only be used with primitive or complex properties. Use the Reference or Collection method.)
ApplicationUser
public class ApplicationUser : IdentityUser
{
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;
}
//Extended Properties
public DateTime? BirthDate { get; set; }
//Key Mappings
public virtual ICollection<Region> Regions { get; set; }
public virtual string DisplayName { get; set; }
public virtual string UserAccountApproverId { get; set; }
public virtual ApplicationUser UserAccountApprover { get; set; }
}
Regions
public class Region:AuditableBase
{
public string RegionCode { get; set; }
public string Description { get; set; }
public virtual ICollection<ApplicationUser> ApplicationUsers { get; set; }
}
Code snippet for Update ApplicationUser
public int ApproveNewUser(UserModel userModel)
{
try
{
ApplicationUser user = new ApplicationUser()
{
Id = userModel.Id,
UserName = userModel.EmailAddress,
LockoutEnabled = false
};
_ctx.Users.Attach(user);
var entry = _ctx.Entry(user);
entry.Property(e => e.LockoutEnabled).IsModified = true;
if (userModel.CheckedRegionsUpdated)
{
AddRegionsToUser(userModel.SelectedRegions, user);
entry.Property(e => e.Regions).IsModified = true;
}
return _ctx.SaveChanges();
}
catch (OptimisticConcurrencyException ex)
{
var objectContext = ((IObjectContextAdapter)_ctx).ObjectContext;
objectContext.Refresh(RefreshMode.ClientWins, _ctx.Users);
return _ctx.SaveChanges();
}
catch (Exception ex)
{
throw ex;
}
}
private void AddRegionsToUser(IList<Region> regionsToAdd, ApplicationUser appUser)
{
appUser.Regions = new List<Region>();
var regionsIds = regionsToAdd.Select(x => x.Id).ToArray<int>();
List<Region> regionssFromDb =
this._ctx.Regions.Where(rg => regionsIds.Contains(rg.Id)).ToList();
foreach (Region region in regionssFromDb)
{
appUser.Regions.Add(region);
}
}
Here is link it is gave me an answer.
Many to Many Relationships not saving
Thanks Lot to Slauma

Entity Framework Code First relationship save Issue

Having some troubles with saving new Employee to database.
Project Model(useless fiels removed):
public class Project{
public virtual ICollection<EmployeeManager.Employee> Employees { get; set; }
}
EmployeeManager.Employee Model:
public class Employee{
public virtual ApplicationUser User { get; set; }
public virtual ProjectsManager.Project ProjectObj { get; set; }
}
Function:
public async Task AddNewEmployeeAsync(string projectId, string email, string fullname){
var projectInfo = await GetByIdAsync(projectId);
var userInfo = new ApplicationUser();
if (await _context.Users.AnyAsync(_ => _.Email == email)){
userInfo = await _context.Users.FirstAsync(_ => _.Email == email);
}
else{
....
userInfo = user;
}
projectInfo.Employees.Add(new EmployeeManager.Employee{
User = userInfo
});
_context.Entry(projectInfo).State = EntityState.Modified;
_context.SaveChanges();
}
And after all that, i have no changes in database.
Why?
If i use _context.Employees.Add() -> it creates new project entity in database.
For Entity Framework to create the correct relationships (one to many) without configuration (FluentApi) you need to make a couple of changes to Employee class
public class Employee
{
public virtual ApplicationUser User { get; set; }
public int ProjectId { get; set; } //foreign key maybe a string as defined in your Project class
public virtual ProjectsManager.Project Project { get; set; }
}

IdentityUser: "Name cannot be null or empty"

I've been trying to inherit IdentityUser to make my own class which uses Identity and still writes to my database and I keep getting this error when I try to call my registration post method:
{
"$id": "1",
"Message": "The request is invalid.",
"ModelState": {
"$id": "2",
"": [
"Name cannot be null or empty."
]
}
}
I tried number of things, but nothing works.For example when I try to set UserName field of IdentityUser it says it's impossible because it doesn't exist in the context.
The important thing to mention would be that I am using ADO.NET database first model for the account :)
This is the class:
public partial class Account :IdentityUser
{
public Account()
{
this.Families = new HashSet<Family>();
}
public long idAccount { get; set; }
public string email { get; set; }
public string password { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public virtual ICollection<Family> Families { get; set; }
}
This is my authentication repository class:
public class AuthRepository :IDisposable
{
private DAKPAKEntities _ctx;
private UserManager<Account> _userManager;
public AuthRepository()
{
_ctx = new DAKPAKEntities();
_userManager = new UserManager<Account>(new UserStore<Account>(_ctx));
}
public async Task<IdentityResult> RegisterUser(Account userModel)
{
Account user = new Account
{
firstName = userModel.firstName,
lastName = userModel.lastName,
email=userModel.email
};
var result = await _userManager.CreateAsync(user,userModel.password);
return result;
}
}
And this is the controller that calls the repository:
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(Account userModel)
{
IdentityResult result = await _repo.RegisterUser(userModel);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
I am new to this, and am out of options to try. I did almost everything that's usually suggested for this type of error, please help :)
It looks like you haven't done everything that is required in order to change the way ASPNet Identity stores the user information in the database.
Suggest you start here: Overview of Custom Storage Providers for ASP.NET Identity

Resources