0

I am working on an application which records users who registers to access some resources on web pages. I'm using ASP.NET MVC 5 with ASP.NET Identity. I use the same value for Email and for a UserName. I have a table which store webpages. I added also a WebpageId column to AspNetUsers table to identify a webpage. Actually, between AspNetUsers table and Webpage table there is many to many relationship, but for some reason I wanted to have multiple records for a user in AspNetUsers table – one for each webpage where the user is registered.

If a user with user@examplemail.com is registered to webpage1 it does not mean that he is registered to webpage2. When he registers to webpage2 with the same e-mail it should be actually completely invisible for him that he is already registered in the system as a user of webpage1.

In order to have multiple records with the same UserName in AspNetUsers table I removed a unique index on UserName and instead I created a unique index on two columns: UserName and WebpageId.

But when I try to register to webpage2 with the e-mail address that is already registered to webpage1, I get DbEntityValidationException with the following validation message : „Username user@examplemail.com has already been taken”. This is thrown in AccountControler.Register() method when UserManger is used to create a new user.

My first try to solve the problem was to customize the UserManager class. I created a subclass of UserManager where I overriden FindByEmailAsync and FindByNameAsync methods in a way that they look for a pair email+webpage or name+webpage:

var existingUser = this.db.Users
                  .FirstOrDefault(x => x.UserName == userName && 
                                       x.WebpageId == webpageId);
return existingUser;

My second try was to additionally customize UserValidator of this UserManager. I do there something similar to the code above - try to get user with the same UserName and webpageId. When user is not found, I return IdentityResult.Success.

No success. Still having DbEntityValidationException with „Username user@examplemail.com has already been taken” validation error.

Any idea which ASP.NET Identity component still looks for a user with a user name (without checking the webpageId)? How should I customize ASP.NET Identity to handle such a scenario?

Here is a part of the exception callstack:

at System.Data.Entity.Internal.InternalContext.SaveChangesAsync(CancellationToken cancellationToken) at System.Data.Entity.Internal.LazyInternalContext.SaveChangesAsync(CancellationToken cancellationToken) at System.Data.Entity.DbContext.SaveChangesAsync(CancellationToken cancellationToken) at System.Data.Entity.DbContext.SaveChangesAsync() at Microsoft.AspNet.Identity.EntityFramework.UserStore6.<SaveChanges>d__31.MoveNext() (...) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at UserRegistrationSystem.Web.Controllers.AccountController.d__11.MoveNext() in i:\Repo\RegistrationSystem\UserRegistrationSystem.Web\Controllers\AccountController.cs:line 126

FYI, I also asked the same question on ASP.NET Identity forum.

1 Answers1

0

What you're describing sounds like multi-tenancy.

I've answered a related question here, and provided a library to implement it

https://github.com/JSkimming/AspNet.Identity.EntityFramework.Multitenant

Community
  • 1
  • 1
James Skimming
  • 4,991
  • 4
  • 26
  • 32
  • Thank you @JamesSkimming! I did not use your library directly. However, I looked at your code and that helped me to make the solution working. Finally I have overriden (following your code): UserManager.FindByEmailAsync, UserStore.FindByNameAsync, UserStore.CreateAsync and IdentityDbContext.ValidateEntity. The latter was the part I was missing. – Paweł Polaczyk Nov 13 '14 at 08:04
  • However, in this setup every action on a User needs TenantId to be known. In my case, that made whole app more complex. I'm going to refactor the application so that AspNetUsers and Webpage have an associative table in between (so no TenantId in AspNetUsers). So if anyone faces a similar problem as in the original post, seriously consider modeling many-to-many relation using the associative table. :P – Paweł Polaczyk Nov 13 '14 at 08:13
  • @paweł-polaczyk the solution I provided makes it possible to have a link table rather than just a row, in that situation the TenantId is a foreign key to the link table. It's all be controlled by overriding OnModelCreating, either way I'm glad my solution was of help. – James Skimming Nov 17 '14 at 13:14