I've used ASP.Net Identity a couple of times now. On a new project I seem to be having an issue creating a user.
When calling _userManager.Create() I get the following error.
The string '{ Name: IX_UserId, Order: 0 }' was not
in the expected format to be deserialized by the
IndexAnnotationSerializer. Serialized values are expected to have
the format '{ Name: 'MyIndex', Order: 7, IsClustered: True,
sUnique: False } { } { Name: 'MyOtherIndex' }'.
I've tried using the following DbContext, which - apart from the class name - is identical to the DbContext i have in another project, that works
public partial class ISIdentityDbContext : IdentityDbContext<IdentityUser>
{
public ISIdentityDbContext()
: base("ISIdentityDbContext")
{ }
public DbSet<ApplicationUserUserInfoMap> ApplicationUserUserInfoMap { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// asp.net identity - call the tables something else..
modelBuilder.Entity<IdentityRole>().ToTable("ApplicationRoles");
modelBuilder.Entity<IdentityUserClaim>().ToTable("ApplicationUserClaims");
modelBuilder.Entity<IdentityUserLogin>().ToTable("ApplicationUserLogins");
modelBuilder.Entity<IdentityUserRole>().ToTable("ApplicationUserRoles");
modelBuilder.Entity<IdentityUser>().ToTable("ApplicationUser");
}
}
I have tried the following:
using (ISIdentityDbContext context = new ISIdentityDbContext())
{
_userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(context));
IdentityUser user = new IdentityUser();
user.UserName = "darren";
_userManager.Create(user, "password");
}
And also, the one I really need to get working as it's extending the ApplicationUser (IdentityUser)
using (ISIdentityDbContext context = new ISIdentityDbContext())
{
_userManager = new UserManager<LegacyApplicationUser>(new UserStore<LegacyApplicationUser>(context));
ApplicationUserUserInfoMap map = new ApplicationUserUserInfoMap();
map.UserGUID = "anIdFromAnotherTable";
LegacyApplicationUser user = new LegacyApplicationUser();
user.UserInfoMap = map;
user.UserName = "darren";
_userManager.Create(user, "password");
}
Where my LegacyApplicationUser is:
public class LegacyApplicationUser : IdentityUser
{
public virtual ApplicationUserUserInfoMap UserInfoMap { get; set; }
}
public class ApplicationUserUserInfoMap
{
public int ID { get; set; }
public string UserGUID { get; set; }
}
I'm totally stumped...no matter whether i rebuild my database to match the standard Identity users or use my extended version i keep getting the same exception shown at the top.
I've likely missed something, though can't figure what....
Any ideas?
Ok - I fixed it!
I was going to remove this question as, as it turns out, it's a very narrow question.
that said, I will leave it in here for anybody else struggling to get EF to play nice with a database that isn't all going through EF.
In our case we have a DB that won't be having EF built against it (it's a very old DB) - but some new parts will be EF'ed; the ASP.Net Identity parts.
It turns out my problem was actually with the __MigrationHistory table.
Once I added a DbInterceptor to my DbContext I could see the actual SQL causing the error.
I removed the entries in the _MigrationHistory table and it all worked.
I have had the same problem
I just create the user without a password then use the password hasher to select the user back out and store it again as a work around. It only fails when i set username and password - i met it in code seeding a database.
Related
I create ASP.NET CORE application with ASP.NET CORE Identity.
I create seed class for saving new users and roles for first startup application. Inside this seed class I get following error when I add Role To User.
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_AspNetUserRoles_AspNetUsers_UserId". The conflict occurred in
database "DB_A14695_Elvinm", table "dbo.AspNetUsers", column 'Id'. The
statement has been terminated.
I used following class for Identity
public class ApplicationUser : IdentityUser
{
public int Type { get; set; }
public int Flags { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime LastModifiedDate { get; set; }
}
And my seed class
public class DbSeeder
{
#region Private Members
private RvMusicalDbContext DbContext;
private RoleManager<IdentityRole> RoleManager;
private UserManager<ApplicationUser> UserManager;
#endregion Private Members
#region Constructor
public DbSeeder(RvMusicalDbContext dbContext, RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
{
DbContext = dbContext;
RoleManager = roleManager;
UserManager = userManager;
}
#endregion Constructor
#region Public Methods
public async Task SeedAsync()
{
// Create the Db if it doesn’t exist
DbContext.Database.EnsureCreated();
// Create default Users
if (await DbContext.Users.CountAsync() == 0) await CreateUsersAsync();
}
#endregion Public Methods
#region Seed Methods
private async Task CreateUsersAsync()
{
// local variables
DateTime createdDate = new DateTime(2016, 03, 01, 12, 30, 00);
DateTime lastModifiedDate = DateTime.Now;
string role_Administrators = "Administrators";
string role_Registered = "Registered";
//Create Roles (if they doesn't exist yet)
if (!await RoleManager.RoleExistsAsync(role_Administrators)) await RoleManager.CreateAsync(new IdentityRole(role_Administrators));
if (!await RoleManager.RoleExistsAsync(role_Registered)) await RoleManager.CreateAsync(new IdentityRole(role_Registered));
// Create the "Admin" ApplicationUser account (if it doesn't exist already)
var user_Admin = new ApplicationUser()
{
UserName = "Admin",
Email = "admin#opengamelist.com",
CreatedDate = createdDate,
LastModifiedDate = lastModifiedDate,
Flags = 0,
Type = 0
};
// Insert "Admin" into the Database and also assign the "Administrator" role to him.
if (await UserManager.FindByIdAsync(user_Admin.Id) == null)
{
await UserManager.CreateAsync(user_Admin, "Pass4Admin");
/// ERROR OCCURED HERE
await UserManager.AddToRoleAsync(user_Admin, role_Administrators);
// Remove Lockout and E-Mail confirmation.
user_Admin.EmailConfirmed = true;
user_Admin.LockoutEnabled = false;
}
#endregion Seed Methods
}
I want to say that roles saved database successfully.
Please help to solve problem.
Check the output of UserManager.CreateAsync call right before the error. I'm guessing your user doesn't get persisted so the next call fails with an FK issue.
If you are using the default identity password requirements (like I was when I tried it) you get a password validation error result from the user creation call.
I know this was answered but I just had to add my solution to my problem in the hopes that I can save some other poor soul from repeating my mistake.....
My user WAS created...
I just wasn't clicking the 'refresh' button on the SQL Server Object Explorer.
:( How embarrassing. I spent an hour+ tracking this down.
Edit: I see I got a negative vote. I had the same problem that the individual asking the question had and my solution was that the page needed refreshed. Although it was a stupid mistake, it's usually safe to assume that when you make a mistake, you are not the only one. My answer is a correct answer in all cases where the user didn't refresh the page, which I believe makes it a decent answer.
I had the same problem. That was the solution; go to DBContext and remove the user in the inherited class.
Before:
public class MyDBContext : IdentityDbContext<MyUser>
{ }
After:
public class MyDBContext : IdentityDbContext
{ }
I ran into the same issue and none of answers here worked, eventually I found solution to that. When you have custom User table verify that in your recently created migration references that point to table AspNetUsers instead point to your custom ApplicationUser table. If not, drop database, manually modify migration file and apply changes.
Should you be ensuring the user admin has an ID
if (await UserManager.FindByIdAsync(user_Admin.Id) == null)
should be
if (await UserManager.FindByIdAsync(user_Admin.Id) != null)
I am building an ASP.NET MVC 5 multi-tenant solution and have a slight problem when it comes to roles. I have created a custom role entity as follows:
public class ApplicationRole : IdentityRole, ITenantEntity
{
public ApplicationRole()
: base()
{
}
public ApplicationRole(string roleName)
: base(roleName)
{
}
public int? TenantId { get; set; }
}
And done everything else needed.. it's all working nicely, except for one thing...; when a tenant admin tries to add a new role and if that role's name is already being used by a role created by another tenant, he will get the following error:
Name Administrators is already taken.
Obviously there is some underlying check for role names to be unique in ASP.NET Identity somewhere. Is there some way to change this so that I can make it look for uniqueness by "TenantId + Name", instead of Name only?
UPDATE
Using dotPeek to decompile the DLLs, I have found that I need to create my own implementation of IIdentityValidator and of course modify my RoleManager. So, here's my role validator:
public class TenantRoleValidator : IIdentityValidator<ApplicationRole>
{
private RoleManager<ApplicationRole, string> Manager { get; set; }
/// <summary>Constructor</summary>
/// <param name="manager"></param>
public TenantRoleValidator(RoleManager<ApplicationRole, string> manager)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
this.Manager = manager;
}
/// <summary>Validates a role before saving</summary>
/// <param name="item"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> ValidateAsync(ApplicationRole item)
{
if ((object)item == null)
{
throw new ArgumentNullException("item");
}
var errors = new List<string>();
await this.ValidateRoleName(item, errors);
return errors.Count <= 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
}
private async Task ValidateRoleName(ApplicationRole role, List<string> errors)
{
if (string.IsNullOrWhiteSpace(role.Name))
{
errors.Add("Name cannot be null or empty.");
}
else
{
var existingRole = await this.Manager.Roles.FirstOrDefaultAsync(x => x.TenantId == role.TenantId && x.Name == role.Name);
if (existingRole == null)
{
return;
}
errors.Add(string.Format("{0} is already taken.", role.Name));
}
}
}
And my role manager:
public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
public ApplicationRoleManager(IRoleStore<ApplicationRole, string> store)
: base(store)
{
this.RoleValidator = new TenantRoleValidator(this);
}
public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
{
return new ApplicationRoleManager(
new RoleStore<ApplicationRole>(context.Get<ApplicationDbContext>()));
}
}
However, I am now getting a new error:
Cannot insert duplicate key row in object 'dbo.AspNetRoles' with unique index 'RoleNameIndex'. The duplicate key value is (Administrators).
The statement has been terminated
I could just modify the db to change the indexes I suppose, but I need it to be correct on installation because the solution I am building is a CMS and will be used for many installations in future...
My first thought is I somehow need to modify the EntityTypeConfiguration<T> for the ApplicationRole entity. But of course I don't have immediate access to that... it just gets auto created by the ApplicationDbContext because it inherits from IdentityDbContext<ApplicationUser>. I will have to delve deeper into the disassembled code and see what I can find...
UPDATE 2
OK, I was using base.OnModelCreating(modelBuilder); to get the configurations for the identity membership tables. I removed that line and copied the decompiled code to my OnModelCreating method, but removed the part for creating the index. This (and removing the index in the db) solved that error I had before.. however, I have 1 more error and I am totally stumped now...
I get an error message as follows:
Cannot insert the value NULL into column 'Name', table 'dbo.AspNetRoles'; column does not allow nulls. INSERT fails.
The statement has been terminated.
This makes no sense, because when debugging, I can clearly see I am passing the Name and the TenantId in the role I am trying to create. This is my code:
var result = await roleManager.CreateAsync(new ApplicationRole
{
TenantId = tenantId,
Name = role.Name
});
Those values are not null, so I don't know what's going on here anymore. Any help would be most appreciated.
UPDATE 3
I created my own RoleStore, which inherits from RoleStore<ApplicationRole> and I overrode the CreateAsync((ApplicationRole role) method so I can debug this part and see what's happening. See below:
After continuing to run the code, I still get the following error on the yellow screen of death:
Someone, anyone, please help shed some light on what's happening here and if it's at all possible to fix this.
UPDATE 4
OK, I'm closer to the answer now.. I created a new db from scratch (allowing EF to create it) and I noticed that the Name column does not get created... only Id and TenantId.. this means the previous error is because my existing DB had the Name column already and was set to NOT NULL.. and EF is ignoring the Name column for my role entity for some reason, which I assume has something to do with it inheriting from IdentityRole.
This is the model configuration I have:
var rolesTable = modelBuilder.Entity<ApplicationRole>().ToTable("AspNetRoles");
rolesTable.Property(x => x.TenantId)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true, Order = 1 }));
rolesTable.Property(x => x.Name)
.IsRequired()
.HasMaxLength(256)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true, Order = 2 }));
rolesTable.HasMany(x => x.Users).WithRequired().HasForeignKey(x => x.RoleId);
I thought it was maybe something to do with the index config, so I just removed both of those (TenantId and Name) and replaced it with this:
rolesTable.Property(x => x.Name)
.IsRequired()
.HasMaxLength(256);
However, the Name column was still not created. The only difference between now and before, is that I am using modelBuilder.Entity<ApplicationRole>() whereas the default would have been modelBuilder.Entity<IdentityRole>() I suppose...
How can I get EF to recognize the both Name property from the base class, IdentityRole and the TenantId property from the derived class ApplicationRole?
OK I've solved this. The answer is to firsrtly follow all the updates I added in my original post and then the final thing to do was make my ApplicationDbContext inherit from IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim> instead of just IdentityDbContext<ApplicationUser>
I'm frustrated. I will try to be specific.
I am kind of a noob when it comes to Identity Authentication, although I managed to finish the entire Identity/Owin setup tutorial from this link
As I said, I managed to finish it in a separate project that I created just to follow the tutorial, and it is working well.
Now, the problem comes when I try to integrate what I learned in a real project WITH THE EXACT SAME CODE THAT I USED IN THE TUTORIAL. A difference in this project is that I have my solution separated in three projects, which are, BusinessLayer, DataObjects, and UI. In this solution, I cannot get passed from the first part of the tutorial, just the "Basics" part, where I should add a user with hardcoded values.
Here is what I have. In the DataObjects project I have an "IdentityModel.cs" where I extend the IdentityUser and IdentityDbContext classes. It looks like this:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int AccountTypeId { get; set; }
public DateTime CreatedOn { get; set; }
public string CreatedBy { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("AdsTracker_DevEntities")
{
}
}
"AdsTracker_DevEntities" is the name of my connectionstring, which is declared in the web.config of the DataObjects project. Then, in the BusinessLayer project, I have a folder called "ProjectStart". In this folder I have a "IdentityConfig.cs" with the following code:
public class ApplicationUserStore : UserStore<ApplicationUser>
{
public ApplicationUserStore(ApplicationDbContext context) : base(context)
{
}
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store) : base(store)
{
}
}
Then, I have a controller in the UI project which simply call the "CreateUser" function in the BusinessLayer. The CreateUser function looks like this:
public async Task<string> AddUser()
{
ApplicationUser user;
ApplicationUserStore Store = new ApplicationUserStore(new ApplicationDbContext());
ApplicationUserManager userManager = new ApplicationUserManager(Store);
user = new ApplicationUser
{
FirstName = "Lilian",
LastName = "Cobian",
AccountTypeId = 1,
UserName = "test.test",
Email = "prueba#gfrmedia.com",
CreatedOn = DateTime.Now
};
var result = await userManager.CreateAsync(user, "P#ssword");
if (!result.Succeeded)
{
return result.Errors.First();
}
return "User Added";
}
And that's it. Now, what is the behavior? If I add an user with the user name "test.test", the result of the "await userManager.CreateAsync(user, "P#ssword");" code is "succeeded", but when I go and check the db, nothing was inserted. Now, if I call the function again, and I try to add the very same user, it returns me the error "User Name already taken". Again, I go to the db, and there is no record at all!!! How can it be taken??
And that is the weird part. In the sole project that I created to follow the tutorial, everything works great and the records are added in the db, but when I try in integrate this into my real project, it behaves just as I mentioned.
Could it be because the connectionstring is declared in a web.config of the DataObjects project and not in the BusinessLayer? But then, why and how it "knows" that I already added "test.test" user if there is no record at all in the db????
Please, this is driving me crazy. Any help will be appreciated.
Thank you!!!
I finally solved my issue. It was writing to a localdb, .mdf located in the AppData folder. Following the couple of suggestions that I received from you guys, I re-checked my web.config. The problem was that I added an .edmx to DataObjects project, pointing to the right SQL Database, which in turns adds a connection string to the right database, but I never added the connection string to the UI web.config file. UI is the main project, so it seems that it's web.config needs also the right connection string.
I think it's already insert into DB, maybe you check the wrong table, using Microsoft Identity it will insert into a special table name start with identity or someting like that.
By the way, this Identity things is suck, so complicate, useless, out of control. It's wasted some time of mine, then i find out authentication just a cookie read write thing, if you do youself, you'll reborn, more google this.
I'm having an issue with the current project I'm working on. It works fine and runs without error with it's current classes and functionality. My issue is that I can't seem to add another model, it's corresponding controller and views and get it to actually work.
Usually I would simply add a new model class to the folder, update my dbcontext class with a dbset with the new model as datatype. Then write "update-database -force" in the package manager console and it would apply the automatic migrations. But for some strange reason I can't seem to comprehend, it simply won't do that anymore.
Instead, after I create the model and add the dbset and then trying to update database it runs the update fine, but it doesn't add any new migrations. The funny thing is when I run the project I get the usual error you always get when you have forgotten to update the database where it's recommending code first migrations etc.
I tried checking my config file and it seems the context key is set to applicationuser instead of the proper dbcontext class, which I'm sensing is why it doesn't detect any changes(usually it figures this out itself?). But when I try changing it to the proper one and updating the database again, it gives me an error saying something about asproles is already in the database?
I'm completely lost here and would appreciate any input an experienced vs13 user can give me.
EDIT:
I should mention I have been working on the identity framework recently, which is probably why it has automatically changed the contextkey? But I haven't had any issues during that with any of my existing classes.
NEW EDIT (29-01-2015)
Relevant part of configuration file:
internal sealed class Configuration : DbMigrationsConfiguration<MEV3.Models.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "MEV3.Models.QuestionContext";
}
protected override void Seed(MEV3.Models.ApplicationDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
this.AddUserAndRoles();
}
My QuestionContext file:
namespace MEV3.Models
{
public class QuestionContext : DbContext
{
public DbSet<ExamSet> ExamSets { get; set; }
public DbSet<BlanketSet> BlanketSets { get; set; }
public DbSet<LooseQuestionCase> LooseQuestionCases { get; set; }
public DbSet<Medcase> Medcases { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<QuestionChoice> QuestionChoices { get; set; }
public DbSet<QuestionBeforeAfter> QuestionBeforeAfters { get; set; }
public DbSet<Answer> Answers { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Institution> Institutions { get; set; }
public DbSet<ExamType> ExamTypes { get; set; }
public DbSet<NewsArticle> NewsArticles { get; set; }
//public DbSet<TaskRecord> TaskRecords { get; set; }
//protected override void OnModelCreating(DbModelBuilder modelBuilder)
//{
// modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//}
}
My ApplicationDbContext file:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<TaskRecord> TaskRecords { get; set; }
}
When I have the contextkey set to my QuestionContext and try to make changes to any models in that dbset, it gives me an error about the taskrecords (which is strange cuz they are inside the applicationdbcontext class).
This means I can't make any succesful updates of the database if I try to change a model in the QuestionContext.
If I set the contextkey to ApplicationDbContext I can make changes to anything in the applicationdbcontext succesfully, but it wont register any changes done in any of the models in the QuestionContext.
I'm at a loss, the way it works now it seems I can only add new models to the applicationdbcontext or change models in it, can't go back and alter anything in the questioncontext. Any thoughts?
Automatic migrations are succesfully enabled on both contexts btw. I've used this setup succesfully before I started fiddling with the identityframework.
Your application has more than one context, therefore you should tell Update-Migration which configuration to chose from. If the two contexts are in different projects, you can use the -ProjectName <string> command. Otherwise, use the -ConfigurationTypeName <string> command, making sure your configuration classes have different names. Also, make sure your QuestionContext's Configuration is public. More information here.
Also, make sure the connection string is pointing to the right Database when you run the project (under your current build configuration).
Finally, I would recommend you Enable-Migrations as forcing it like you do can lead to data loss.
So I am trying to build Custom membership using EF. I dont really know what i am doing but it has gone fairly smooth so far.
I am on the Database Initializer step where i am trying to dump data into the database soon as the project runs. My class Application.cs uses a GUID as a primary key as seen below. I am trying to figure out how i can add that GUID into the database.
I don't know if this is possible but this is what i am trying to do. I took the default login's Database you get when you make a normal web application project in VS 2012 and trying to recreate that database using EF Code First(For practice). This is what i got so far.
Class
public class Applications
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ApplicationId { get; set; }
[StringLength(125)]
public string ApplicationName { get; set; }
}
Intializer to dump data into db on build(not including Seed.)
private static List<Applications> addApplications()
{
var apps = new List<Applications>
{
new Applications
{
ApplicationId = Guid.NewGuid(),
ApplicationName = "Test Login"
}
};
return apps;
}
private static List<Memberships> addMemberships()
{
var mem = new List<Memberships>
{
new Memberships
{
UserId = Guid.NewGuid(),
ApplicationId = ?, // How can this guid Match the one in
// in ApplicationId for the Application Table?
}
};
return mem;
}
I get "Invalid Initializer member declarator". The problem i face is that I need the GUIDS to be the same for ApplicationId across multiple tables. I don't even know if this is possible or right?
I got a feeling I have to share it somehow maybe like
Guid AppId;
AppId = Guid.NewGuid();
In your Membership model instead of storing the GUID "ApplicationId" to try and reference the application you should use a navigation property like so (see this link for better description of navigation properties):
public class Memberships
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UserId { get; set; }
//if you set your model up like this entity framework will take care of creating
/the foreign key for you.
public Application MemberApplication { get; set; }
}
then just pass in the appropriate application to your method like so:
private static List<Memberships> addMemberships(Application app)
{
var mem = new List<Memberships>
{
new Memberships
{
UserId = Guid.NewGuid(),
Application = app,
}
};
return mem;
}
Setting your model up like this lets you take full advantage of oop and relational database. Hope that helps.