C# 如何允许用户使用Identity Framework 1.0注册重复用户名

C# 如何允许用户使用Identity Framework 1.0注册重复用户名,c#,asp.net-mvc,asp.net-identity,C#,Asp.net Mvc,Asp.net Identity,我想使用Identity Framework 1.0在MVC中开发一个应用程序,它允许用户使用其他用户使用的相同用户名注册 删除用户时,我希望将其IsDeleted自定义属性设置为true,而不是从数据库中删除该用户。在这种情况下,另一个用户可以使用其IsDeleted设置为true的用户的UserName 但是默认的UserManager.CreateAsync(用户,密码)方法正在阻止执行此操作 我像这样重写了IdentityDbContext的ValidateEntity方法 protec

我想使用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);
}
受保护的重写DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,IDictionary项)
{
if((entityEntry!=null)&&(entityEntry.State==EntityState.Added))
{
ApplicationUser user=entityEntry.Entity作为ApplicationUser;
if((user!=null)&&this.Users.Any(u=>string.Equals(u.UserName,user.UserName)&&u.IsDeleted==false))
{
返回新的DbEntityValidationResult(entityEntry,new List()){
ValidationErrors={
新的DbValidationError(“用户”,string.Format(CultureInfo.CurrentCulture,
“”,新对象[]{user.UserName}))
} 
};
}
IdentityRole角色=entityEntry。实体为IdentityRole;
if((role!=null)&&this.Roles.Any(r=>string.Equals(r.Name,role.Name)))
{
返回新的DbEntityValidationResult(entityEntry,new List()){
ValidationErrors={
新的DbValidationError(“角色”,string.Format(CultureInfo.CurrentCulture,
“”,新对象[]{role.Name}}};
}
}
返回基数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);
}
公共异步任务寄存器(RegisterViewModel模型)
{
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(用户、模型、密码);
if(result.successed)
{
TempData[“MessageConfirm”]=user.Email;
返回重定向操作(“确认”、“账户”);
}
其他的
{
加法器(结果);
}
}
//如果我们走到这一步,有些东西失败了,重新显示形式
返回视图(模型);
}
ValidateEntity
方法应在
wait\u accountService.CreateAsync(用户、型号、密码)时执行执行。但是它是在
register
方法完成执行之后执行的。所以结果会抛出错误


有什么建议可以让我做到这一点吗?

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

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

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

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

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

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

}
public override任务findbyemailsync(字符串电子邮件){
返回此.GetUserAggregateAsync(u=>u.Email.ToUpper()==Email.ToUpper())
&&u.TenantId==此.TenantId);
}
findbynamesync

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

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

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

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

}
public override任务FindByNameAsync(字符串用户名){
返回此.GetUserAggregateAsync(u=>u.UserName.ToUpper()==UserName.ToUpper())
&&u.TenantId==此.TenantId);
}
用户验证程序 虽然默认UserValidator对重复用户名进行硬编码检查,但我们新实现的UserStore方法FindByNameAsync和FindByEmailAsync将允许正确的多租户行为(假设您在UserStore中设置了租户)。这意味着我们可以充分利用默认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 }));
        }
    }
公共类ApplicationUserDbContext:IdentityDbContext
其中TUser:ApplicationUser{
公共应用程序UserDBContext(字符串名称或连接字符串)
:base(名称或连接字符串){
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder){
基于模型创建(modelBuilder);
var user=modelBuilder.Entity();
用户.道具