c# – 如何允许用户使用Identity Framework 1.0注册重复的UserName

我想使用Identity Framework 1.0在MVC中开发一个应用程序,它允许用户使用其他用户使用的相同用户名进行注册.

删除用户时,我想将其IsDeleted定制属性设置为true,而不是从数据库中删除用户.在这种情况下,另一个用户可以使用IsDeleted设置为true的用户的UserName.

但默认的UserManager.CreateAsync(用户,密码);方法是阻止这样做.

我已经像这样重写了IdentityDbContext的ValidateEntity方法

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);
}

这是我创建用户的注册方法

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);
}

当await _accountService.CreateAsync(user,model.Password)时,应该执行ValidateEntity方法;执行.但它在register方法完成执行后执行.所以结果会引发错误.

有什么建议我怎么能做到这一点?

最佳答案
我的烦恼是当人们决定提出建议而不是回答问题时.用户名不必是唯一的. db密钥是userid而不是用户名.如果您有为多家公司提供服务的应用程序,则不同公司的员工可能拥有相同的用户名.为了做到这一点,你必须扩展aspnet标识.

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

IdentityUser
首先,我们需要通过扩展IdentityUser类来添加TenantId的声明(您可以将其重命名为符合您的业务要求).虽然这在概念上是一个声明,但我们将利用AspNetUser表并添加TenantId作为属性,因为我们将对此属性进行一些调查.为简单起见,我已将TenantId添加为int,但非迭代替代方法是使用字符串.

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

UserStore
接下来,我们将为了解我们的新属性的新用户实现UserStore.这里我们在UserStore类中使用一个属性来设置我们的TenantId,允许我们用我们的多租户实现来覆盖基础实现.

    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
虽然默认的UserValidator具有对重复用户名的硬编码检查,但我们对UserStore方法FindByNameAsync和FindByEmailAsync的新实现将允许正确的多租户行为(假设您已在UserStore中设置了TenantId).这意味着我们可以充分利用默认的UserValidator并在必要时进行扩展.

IdentityDbContext
现在这里有点尴尬. ASP.NET Identity团队再次对IdentityDbContext类中的重复用户名进行了硬编码检查,但这次它使用索引在ValidateEntity方法和EF数据库模式本身中.

可以通过扩展OnModelCreating方法来更改基于用户名的唯一索引来查找索引,以查找我们的TenantId(复合索引).这样可以节省我们丢失这个有用的索引并优化我们的数据库以实现多租户.您可以使用以下覆盖方法执行此操作:

    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>());
    }

客户
现在剩下的就是初始化类.不要忘记,每当您新上下文时,您都需要提供TenantId.请参阅下面的示例(注意使用’example’,这些类都是一次性的……).

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

转载注明原文:c# – 如何允许用户使用Identity Framework 1.0注册重复的UserName - 代码日志