2015-03-17 7 views
5

Chcę utworzyć aplikację w MVC przy użyciu programu Identity Framework 1.0, który umożliwia użytkownikom rejestrowanie się przy użyciu tej samej nazwy użytkownika, jakiej użył inny użytkownik.Jak zezwolić użytkownikowi na rejestrację z powieloną nazwą użytkownika za pomocą aplikacji Identity Framework 1.0

Podczas usuwania użytkownika chcę ustawić jego właściwość niestandardową na wartość true, zamiast usuwać użytkownika z bazy danych. W tym przypadku inny użytkownik może użyć UserName użytkownika, którego IsDeleted jest ustawiony na true.

Ale domyślna metoda UserManager.CreateAsync(user, password); uniemożliwia wykonanie tej czynności.

miałem nadpisane metody IdentityDbContextValidateEntity jak ten

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items) 
{ 
    if ((entityEntry != null) && (entityEntry.State == EntityState.Added)) 
    { 
     ApplicationUser user = entityEntry.Entity as ApplicationUser; 
     if ((user != null) && this.Users.Any<ApplicationUser>(u => string.Equals(u.UserName, user.UserName) && u.IsDeleted==false)) 
     { 
      return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { 
       ValidationErrors = { 
        new DbValidationError("User", string.Format(CultureInfo.CurrentCulture, 
         "", new object[] { user.UserName })) 
       } 
      }; 
     } 
     IdentityRole role = entityEntry.Entity as IdentityRole; 
     if ((role != null) && this.Roles.Any<IdentityRole>(r => string.Equals(r.Name, role.Name))) 
     { 
      return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()) { 
       ValidationErrors = { 
        new DbValidationError("Role", string.Format(CultureInfo.CurrentCulture, 
         "", new object[] { role.Name })) } }; 
     } 
    } 
    return base.ValidateEntity(entityEntry, items); 
} 

Oto moja metoda rejestrze, w którym użytkownik jest tworzony

public async Task<ActionResult> Register(RegisterViewModel model) 
{ 
    if (ModelState.IsValid) 
    { 
     var user = new ApplicationUser() { UserName = model.UserName, Email = model.Email.ToLower(), CreatedBy = model.UserName, CreatedDate = DateTime.UtcNow, }; 

     user.ConfirmedEmail = false; 
     var result = await _accountService.CreateAsync(user, model.Password); 
     if (result.Succeeded) 
     { 

      TempData["MessageConfirm"] = user.Email; 
      return RedirectToAction("Confirm", "Account"); 
     } 
     else 
     { 
      AddErrors(result); 
     } 
    } 
    // If we got this far, something failed, redisplay form 
    return View(model); 
} 

ValidateEntity metody powinny być wykonywane, gdy await _accountService.CreateAsync(user, model.Password); wykonany. Ale wykonuje się po tym, jak metoda register zakończy jego wykonywanie. Więc wynik rzuca błąd.

Jakieś sugestie, jak mogę to osiągnąć?

+2

Nazwy użytkowników ** muszą ** być unikatowe! Jaki problem próbujesz rozwiązać! –

+2

Nie chcę, aby nazwa użytkownika była unikalna. – Twix

+0

Ponieważ nazwa użytkownika jest odpowiednikiem klucza podstawowego i jest jedynym identyfikatorem dla użytkownika, musi ** musi być unikalna. Nie masz wyboru. Jak inaczej rozróżniać użytkowników. Więc, jaki jest podstawowy problem, który próbujesz rozwiązać? –

Odpowiedz

3

Mój zwierzak obawia się, gdy ludzie decydują się udzielić porady zamiast odpowiadać na zadane pytanie. nazwy użytkowników nie muszą być unikatowe. Klucz db jest identyfikatorem użytkownika, a nie nazwą użytkownika. Jeśli masz aplikację obsługującą kilka firm, istnieje szansa, że ​​pracownicy różnych firm będą mieli tę samą nazwę użytkownika. W tym celu musisz rozszerzyć tożsamość aspnet.

https://www.scottbrady91.com/ASPNET-Identity/Quick-and-Easy-ASPNET-Identity-Multitenancy

IdentityUser Na początek musimy dodać roszczenia TenantId (można przemianować to jak pasuje do Twoich wymagań biznesowych) poprzez rozszerzenie klasy IdentityUser. Chociaż jest to koncepcyjnie roszczenie, skorzystamy z tabeli AspNetUser i dodamy TenantId jako właściwość, ponieważ będziemy odpytywać według tej właściwości. Dla uproszczenia dodałem tenantId jako int, jednak nieintenatywną alternatywą byłoby użycie ciągu znaków.

public class ApplicationUser : IdentityUser { 
    public int TenantId { get; set; } 
} 

UserStore Następny będziemy realizować UserStore dla naszego nowego użytkownika, który zdaje sobie sprawę z naszej nowej nieruchomości. Tutaj używamy właściwości w naszej klasie UserStore, aby ustawić nasz TenantId, co pozwala nam zastąpić podstawową implementację naszą wielo-dzierżawioną implementacją.

public class ApplicationUserStore<TUser> : UserStore<TUser> 
    where TUser : ApplicationUser { 
    public ApplicationUserStore(DbContext context) 
     : base(context) { 
    } 

    public int TenantId { get; set; } 
} 

CreateUserAsync

public override Task CreateAsync(TUser user) { 
if (user == null) { 
    throw new ArgumentNullException("user"); 
} 

user.TenantId = this.TenantId; 
return base.CreateAsync(user); 

} 

FindByEmailAsync

public override Task<TUser> FindByEmailAsync(string email) { 
return this.GetUserAggregateAsync(u => u.Email.ToUpper() == email.ToUpper() 
    && u.TenantId == this.TenantId); 

} 

FindByNameAsync

public override Task<TUser> FindByNameAsync(string userName) { 
return this.GetUserAggregateAsync(u => u.UserName.ToUpper() == userName.ToUpper() 
    && u.TenantId == this.TenantId); 

} 

UserValidator Podczas domyślnej UserValidator został ustalony sprawdza zduplikowanych nazw użytkowników, nasza nowa implementacja metod UserStore FindByNameAsync i FindByEmailAsync pozwoli na prawidłowe zachowanie wieloma najemcami (zakładając, że masz ustawić TenantId obrębie UserStore). Oznacza to, że możemy w pełni wykorzystać domyślną wartość UserValidator i w razie potrzeby ją rozszerzyć.

IdentityDbContext Teraz jest trochę niezręczny. Zespół ASP.NET Identity ponownie zakodował sprawdzanie duplikatów nazw użytkowników w klasie IdentityDbContext, jednak tym razem zarówno w metodzie ValidateEntity, jak iw samym schemacie bazy danych EF za pomocą indeksu.

Indeks można rozwiązać, rozszerzając metodę OnModelCreating, aby zmienić unikatowy indeks na podstawie nazwy użytkownika, aby również znaleźć nasz TenantId (indeks złożony). Dzięki temu tracimy ten przydatny indeks i optymalizujemy naszą bazę danych pod kątem wielopoziomowości. Można to zrobić w następujący sposób pominąć:

public class ApplicationUserDbContext<TUser> : IdentityDbContext<TUser> 
    where TUser : ApplicationUser { 
    public ApplicationUserDbContext(string nameOrConnectionString) 
     : base(nameOrConnectionString) { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) { 
     base.OnModelCreating(modelBuilder); 

     var user = modelBuilder.Entity<TUser>(); 

     user.Property(u => u.UserName) 
      .IsRequired() 
      .HasMaxLength(256) 
      .HasColumnAnnotation("Index", new IndexAnnotation(
       new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 1})); 

     user.Property(u => u.TenantId) 
      .IsRequired() 
      .HasColumnAnnotation("Index", new IndexAnnotation(
       new IndexAttribute("UserNameIndex") { IsUnique = true, Order = 2 })); 
    } 
} 
The ValidateEntity method is a bit more tricky however, as we will have to reimplement the entire method in order to remove the hardcoded username checks: 

    protected override DbEntityValidationResult ValidateEntity(
     DbEntityEntry entityEntry, IDictionary<object, object> items) { 
     if (entityEntry != null && entityEntry.State == EntityState.Added) { 
      var errors = new List<DbValidationError>(); 
      var user = entityEntry.Entity as TUser; 

      if (user != null) { 
       if (this.Users.Any(u => string.Equals(u.UserName, user.UserName) 
        && u.TenantId == user.TenantId)) { 
        errors.Add(new DbValidationError("User", 
         string.Format("Username {0} is already taken for AppId {1}", 
         user.UserName, user.TenantId))); 
       } 

       if (this.RequireUniqueEmail 
        && this.Users.Any(u => string.Equals(u.Email, user.Email) 
        && u.TenantId == user.TenantId)) { 
        errors.Add(new DbValidationError("User", 
         string.Format("Email Address {0} is already taken for AppId {1}", 
         user.UserName, user.TenantId))); 
       } 
      } 
      else { 
       var role = entityEntry.Entity as IdentityRole; 

       if (role != null && this.Roles.Any(r => string.Equals(r.Name, role.Name))) { 
        errors.Add(new DbValidationError("Role", 
         string.Format("Role {0} already exists", role.Name))); 
       } 
      } 
      if (errors.Any()) { 
       return new DbEntityValidationResult(entityEntry, errors); 
      } 
     } 

     return new DbEntityValidationResult(entityEntry, new List<DbValidationError>()); 
    } 

Client Pozostaje teraz jest do zainicjowania klas. Nie zapominaj, że będziesz musiał dostarczyć TenantId za każdym razem, gdy zmienisz kontekst. Zobacz poniższy przykład (zwróć uwagę na użycie "przykładu", wszystkie te klasy są jednorazowe ...).

var context = new ApplicationUserDbContext<ApplicationUser>("DefaultConnection"); 
var userStore = new ApplicationUserStore<ApplicationUser>(context) { TenantId = 1 }; 
var userManager = new UserManager<ApplicationUser, string>(userStore); 
Powiązane problemy