IdentityRole in multi-tenant application - c#

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>

Related

Custom IdentityUser class property/dependent entity is always null

I am using ASP.NET Core targeting NET6.0 using the Microsoft.AspNetCore.Identity user management system with EntityFrameworkCore.
I have a custom dependent entity (Players) which I want to link to my custom ApplicationUser class in a one-to-one relationship. Every AspNetUsers record has an associated single record in the previously existing my_db_schema.tblPlayers database table. The AspNetCore identity tables are in the same database. Both are populated with real data (This project is to migrate from AspNetIdentity to AspNetCore.Identity)
[Table("tblPlayers", Schema = "my_db_schema")]
public class Players
{
//[Key]
public int Id { get; set; }
public string PlayerName { get; set; }
//[ForeignKey("User")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
The UserId is a foreign key to the Id column on the AspNetUsers table. The Id column is a primary key.
My custom ApplicationUser class is defined as this:
public class ApplicationUser : IdentityUser<string>
{
public Players Players { get; set; }
}
In my custom ApplicationDbContext OnModelCreating function, I have the following:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Players>().HasKey(t => t.Id);
modelBuilder.Entity<ApplicationUser>()
.HasOne<Players>(s => s.Players)
.WithOne(u => u.User)
.HasForeignKey<Players>(ad => ad.UserId).HasPrincipalKey<ApplicationUser>(x => x.Id).IsRequired();
}
I am not interested in any automatic removal/management of data in the tblPlayers database table. I am not interested in running migrations or scaffolding. All that I need is that for any place in my code where I get an instance of an ApplicationUser, that the Players property is populated with data from the tblPlayers database table. No matter what combination of fluid API, or annotations I use, the ApplicationUser.Players property is always null, and the rest of the ApplicationUser properties are populated as expected.
I have seen these articles:
Why custom properties for IdentityUser custom class are always null
Custom property is null IdentityUser
But both of these require a specific instantiation of the custom ApplicationUserManager class. I would like to avoid having to do that in all areas of my code where ApplicationUserManager is referenced if possible.
Please help :)
EDIT: I have implemented my own custom ApplicationUserManager already.
Implementing a custom UserManager is trivial -
public class CustomUserManager : UserManager<User>
Then register it at startup
services.AddIdentity<User, Role>()
.AddUserManager<QuestUserManager>()
From there you'll just inject the custom user manager class where you need it.
public class SomeController : Controller {
private readonly CustomUserManager _userManager;
public SomeController(CustomUserManager userManager)
{
_userManager = userManager;
}
}
The only way you'll get the Players property populated is to override the method(s) that return a User and add .Include(u => u.Players) to the EF Core queries - something like
public override async Task<User> FindByIdAsync(string userId)
return await Users.Include(u => u.Players).SingleOrDefaultAsync(u => u.Id == userId);
}
You don't have to create your own custom UserManager in order to do that. But #Mr.T is right, it is necessary to include the dependant table in order to retrieve records filled.
So you have 3 options, except creating a custom UserManager:
In case you are following the Repository pattern you would do something like the following:
//Yor 'UserRepository' or whatever
public async Task<List<ApplicationUser>> GetUsers()
{
var set = _context.Set<ApplicationUser>()
.Include(user => user.Players);
return await set.ToListAsync();
}
In case you are using the DbContext directly - you would basically do the same as within the method above.
And in case you are using the default UserManager:
var filledUsers = await _userManager.Users.Include(user => user.Players).ToListAsync();

Entity Framework Core Find and Update single value [duplicate]

What is the best approach to update database table data in Entity Framework Core?
Retrieve the table row, do the changes and save
Use keyword Update in DB context and handle exception for item not exist
What are the improved features we can use over EF6?
To update an entity with Entity Framework Core, this is the logical process:
Create instance for DbContext class
Retrieve entity by key
Make changes on entity's properties
Save changes
Update() method in DbContext:
Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges() is called.
Update method doesn't save changes in database; instead, it sets states for entries in DbContext instance.
So, We can invoke Update() method before to save changes in database.
I'll assume some object definitions to answer your question:
Database name is Store
Table name is Product
Product class definition:
public class Product
{
public int? ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public decimal? UnitPrice { get; set; }
}
DbContext class definition:
public class StoreDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Your Connection String");
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(entity =>
{
// Set key for entity
entity.HasKey(p => p.ProductID);
});
base.OnModelCreating(modelBuilder);
}
}
Logic to update entity:
using (var context = new StoreDbContext())
{
// Retrieve entity by id
// Answer for question #1
var entity = context.Products.FirstOrDefault(item => item.ProductID == id);
// Validate entity is not null
if (entity != null)
{
// Answer for question #2
// Make changes on entity
entity.UnitPrice = 49.99m;
entity.Description = "Collector's edition";
/* If the entry is being tracked, then invoking update API is not needed.
The API only needs to be invoked if the entry was not tracked.
https://www.learnentityframeworkcore.com/dbcontext/modifying-data */
// context.Products.Update(entity);
// Save changes in database
context.SaveChanges();
}
}
According to Microsoft docs:
the read-first approach requires an extra database read, and can result in more complex code for handling concurrency conflict
However, you should know that using Update method on DbContext will mark all the fields as modified and will include all of them in the query. If you want to update a subset of fields you should use the Attach method and then mark the desired field as modified manually.
context.Attach(person);
context.Entry(person).Property(p => p.Name).IsModified = true;
context.SaveChanges();
public async Task<bool> Update(MyObject item)
{
Context.Entry(await Context.MyDbSet.FirstOrDefaultAsync(x => x.Id == item.Id)).CurrentValues.SetValues(item);
return (await Context.SaveChangesAsync()) > 0;
}
It's super simple
using (var dbContext = new DbContextBuilder().BuildDbContext())
{
dbContext.Update(entity);
await dbContext.SaveChangesAsync();
}
Microsoft Docs gives us two approaches.
Recommended HttpPost Edit code: Read and update
This is the same old way we used to do in previous versions of Entity Framework. and this is what Microsoft recommends for us.
Advantages
Prevents overposting
EFs automatic change tracking sets the Modified flag on the fields that are changed by form input.
Alternative HttpPost Edit code: Create and attach
an alternative is to attach an entity created by the model binder to the EF context and mark it as modified.
As mentioned in the other answer the read-first approach requires an extra database read, and can result in more complex code for handling concurrency conflicts.
Assume we have an entity Student and AppDbContext as follows.
class Student
{
public int Id { get; set; }
public string Name { get; set; } = default!;
public int Age { get; set; }
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> opts) : base(opts) { }
public DbSet<Student> Students { get; set; }
}
Version A
CurrentValues can only work for a tracked entity (found).
Only the changed properties are marked as Modified.
Automatic property mapping that is useful when using type parameter TEntity instead of a fixed type Student.
async Task Edit_A(int id, Student incoming, AppDbContext db)
{
if (await db.Students.FindAsync(id) is Student found)
{
db.Entry(found).CurrentValues.SetValues(incoming);
await db.SaveChangesAsync();
}
}
Version B
It works only on a tracked entity (found).
It is not necessary to map all properties because only the changed properties are marked as Modified.
Manual property mapping so we cannot not use generic type parameter.
async Task Edit_B(int id, Student incoming, AppDbContext db)
{
if (await db.Students.FindAsync(id) is Student found)
{
found.Name = incoming.Name;
found.Age = incoming.Age;
await db.SaveChangesAsync();
}
}
Version C
Update() works only on an untracked entity (incoming) and makes it tracked. Untracking found before invoking Update(incoming) is mandatory because only one entity can be tracked with the given primary key.
All properties (including unchanged ones) are marked as Modified. It is less efficient.
Automatic property mapping that is useful for generic type parameter.
async Task Edit_C(int id, Student incoming, AppDbContext db)
{
if (await db.Students.FindAsync(id) is Student found)
{
db.Students.Entry(found).State = EntityState.Detached;
db.Students.Update(incoming);
await db.SaveChangesAsync();
}
}
Version D
It is the same as version C. I rewrite again below for the sake of completeness.
It works only on an untracked entity (incoming) and makes it tracked. Untracking found is mandatory because only one entity can be tracked with the given primary key.
All properties (including unchanged ones) are marked as Modified. It is less efficient.
Automatic property mapping that is useful for generic type parameter.
async Task Edit_D(int id, Student incoming, AppDbContext db)
{
if (await db.Students.FindAsync(id) is Student found)
{
db.Students.Entry(found).State = EntityState.Detached;
db.Students.Entry(incoming).State = EntityState.Modified;
await db.SaveChangesAsync();
}
}
Version E
It works only on an untracked entity (incoming) and makes it tracked. Untracking found is mandatory because only one entity can be tracked with the given primary key.
It is not necessary to map all properties because only properties (including unchanged ones) marked with IsModified=true will be updated. It is less efficient if you mark IsModified=true for unchanged properties.
Manual property mapping so we cannot not use generic type parameter.
async Task Edit_E(int id, Student incoming, AppDbContext db)
{
if (await db.Students.FindAsync(id) is Student found)
{
db.Students.Entry(found).State = EntityState.Detached;
db.Students.Entry(incoming).Property(s => s.Name).IsModified = true;
db.Students.Entry(incoming).Property(s => s.Age).IsModified = true;
await db.SaveChangesAsync();
}
}
I set it as a Community Wiki, feel free to edit as many as you want.
After going through all the answers I thought i will add two simple options
If you already accessed the record using FirstOrDefault() with tracking enabled (without using .AsNoTracking() function as it will disable tracking) and updated some fields then you can simply call context.SaveChanges()
In other case either you have entity posted to server using HtppPost or you disabled tracking for some reason then you should call context.Update(entityName) before context.SaveChanges()
1st option will only update the fields you changed but 2nd option will update all the fields in the database even though none of the field values were actually updated :)
A more generic approach
To simplify this approach an "id" interface is used
public interface IGuidKey
{
Guid Id { get; set; }
}
The helper method
public static void Modify<T>(this DbSet<T> set, Guid id, Action<T> func)
where T : class, IGuidKey, new()
{
var target = new T
{
Id = id
};
var entry = set.Attach(target);
func(target);
foreach (var property in entry.Properties)
{
var original = property.OriginalValue;
var current = property.CurrentValue;
if (ReferenceEquals(original, current))
{
continue;
}
if (original == null)
{
property.IsModified = true;
continue;
}
var propertyIsModified = !original.Equals(current);
property.IsModified = propertyIsModified;
}
}
Usage
dbContext.Operations.Modify(id, x => { x.Title = "aaa"; });
Personally I see the operation that you are doing is an upsert operation, where if data already exist we update, else we insert. There is one good library from Flexlab to support Upsert operation with this syntax
var country = new Country
{
Name = "Australia",
ISO = "AU",
Created = DateTime.UtcNow,
};
await DataContext.Upsert(country)
.On(c => c.ISO)
.UpdateColumns(c => new Country
{
Name = "Australia"
Updated = DateTime.UtcNow,
})
.RunAsync();
The code will check for the property Country.ISO in the table. If it does not exist yet it will insert new row altogether into the table. Else if any row with the same Country.ISO already exist it will update columns Country.Name and Country.Updated for that row.
This method is very fast because we only do one call to the database instead of two calls to check if the data already exist before we updating or inserting the data.
Note that this answer does not apply to you if your intend is not to do the Upsert operation

Asp.net core Identity "The INSERT statement conflicted with the FOREIGN KEY constraint "

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)

Problems with updating many-to-many relationships in Entity Framework 6 (Code First)

I have a simple table setup
User
- Id
- Name
Role
- Id
- Name
UserRole
- UserId
- RoleId
Using Fluent I have the following relationship declared
this.HasMany(t => t.Roles)
.WithMany(s => s.Users)
.Map(m =>
{
m.ToTable("UserRole");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
When I create a new User with multiple Role it adds them correctly in the Database.
When I Update a User with Role's that don't already exist yet then it is OK.
I am having problems adding the following:
Existing Roles
Role A
Role B
Role C
I want to remove Role B thus leaving
Role A
Role C
I have tried a couple of things but I always end up with the "Additional information: Saving or accepting changes failed because more than one entity of type '...Role' have the same primary key value." or no deletion occurs at all.
Method 1
I have tried things like creating a method to delete all the Roles first then just adding the incoming Roles but for some reason it's maintaining the tracked changes and still sees the Roles that are deleted.
public ActionResult Edit(int userId, User user)
{
// clear roles
_userService.ClearRoles(userId);
_unitOfWork.Save();
// if I put a break here, I can see the roles being removed from the database
// update user with new roles
_userService.Update(id, user)
_unitOfWork.Save();
}
// _userService.Update
public void Update(int userId, User user)
{
User existingUser = Find(userId);
if (existingUser != null)
{
existingUser.Roles = user.Roles;
_userRepository.Update(existingUser);
}
}
public void ClearRoles(int userId)
{
User existingUser = GetUser(userId);
if(existingUser != null)
{
existingUser.Roles.ToList().ForEach(f => existingUser.Roles.Remove(f));
}
}
Method 2
I tried to remove the Role objects but no Roles get deleted, nothing happens
public ActionResult Edit(int userId, User user)
{
_userService.Update(id, user)
_unitOfWork.Save();
}
// _userService.Update
public void Update(int userId, User user)
{
User existingUser = Find(userId);
if (existingUser != null)
{
existingUser.Roles.ToList().ForEach(f => existingUser.Roles.Remove(f));
existingUser.Roles = user.Roles;
_userRepository.Update(existingUser);
}
}
Any further ideas on how to resolve this?
Concering the error: "Saving or accepting changes failed because more
than one entity of type '' have the same primary key value"...
This often occurs when a dbcontext, having several same objects orginated from a different db context, is flushed towards the database.
Example
public void Save(PlcVariable plcVariable)
{
try
{
using (var context = new XBSDbDataContext())
{
plcVariable.PlcVariableMeasure.LineObjects // Plc variable has a selfreference that has a list of lineobject, that already has a line object with id = 1
var lineobject = new LineObjectService().GetById(1);//=> this line object is orginated from another Dbcontext.
plcVariable.LineObjects.Add(lineobject);
context.SaveChanges(); // error occurs
// EF will see the added line object as a different object, because it is coming from another dbcontext, than the same object(equal id's) that is already present.
}
You can try to assure to load object with one dbContext.

ASP.Net Identity not creating user

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.

Categories

Resources