简体   繁体   English

多租户应用程序中的IdentityRole

[英]IdentityRole in multi-tenant application

I am building an ASP.NET MVC 5 multi-tenant solution and have a slight problem when it comes to roles. 我正在构建一个ASP.NET MVC 5多租户解决方案,并且在角色方面存在一些问题。 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. 显然,某些基础检查角色名称在ASP.NET身份中是唯一的。 Is there some way to change this so that I can make it look for uniqueness by "TenantId + Name", instead of Name only? 有没有办法改变这一点,以便我可以通过“TenantId + Name”来寻找唯一性,而不仅仅是Name?

UPDATE 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. 使用dotPeek反编译DLL,我发现我需要创建自己的IIdentityValidator实现,当然还要修改我的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'. 无法在具有唯一索引“RoleNameIndex”的对象“dbo.AspNetRoles”中插入重复的键行。 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... 我可以修改数据库以更改我想要的索引,但我需要它在安装时是正确的,因为我正在构建的解决方案是CMS,将来会用于许多安装......

My first thought is I somehow need to modify the EntityTypeConfiguration<T> for the ApplicationRole entity. 我的第一个想法是我需要修改ApplicationRole实体的EntityTypeConfiguration<T> 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> . 但是我当然没有立即访问它...它只是由ApplicationDbContext自动创建,因为它继承自IdentityDbContext<ApplicationUser> I will have to delve deeper into the disassembled code and see what I can find... 我将不得不深入研究反汇编的代码,看看我能找到什么......

UPDATE 2 更新2

OK, I was using base.OnModelCreating(modelBuilder); 好的,我正在使用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. 我删除了该行并将反编译的代码复制到我的OnModelCreating方法,但删除了用于创建索引的部分。 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'; 无法将值NULL插入列'Name',表'dbo.AspNetRoles'; column does not allow nulls. 列不允许空值。 INSERT fails. INSERT失败。 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. 这没有任何意义,因为在调试时,我可以清楚地看到我正在尝试创建的角色中传递Name和TenantId。 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 更新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: 我创建了自己的RoleStore,它继承了RoleStore<ApplicationRole>并且我覆盖了CreateAsync((ApplicationRole role)方法,所以我可以调试这个部分,看看发生了什么。见下文:

在此输入图像描述

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 更新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. 好的,我现在更接近答案..我从头开始创建了一个新的数据库(允许EF创建它),我注意到Name列没有被创建...只有Id和TenantId ..这意味着之前的错误是因为我的现有数据库已经具有Name列并且被设置为NOT NULL ..并且由于某种原因,EF忽略了我的角色实体的Name列,我认为这与继承自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: 我认为这可能与索引配置有关,所以我只删除了这两个(TenantId和Name)并将其替换为:

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... 现在和之前的唯一区别是,我使用的是modelBuilder.Entity<ApplicationRole>()而默认值是modelBuilder.Entity<IdentityRole>()我想...

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 ? 我怎样才能得到EF承认从基类,两个Name属性IdentityRole从派生类和TenantId财产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> 答案是首先遵循我在原始帖子中添加的所有更新,然后最后要做的是让我的ApplicationDbContext继承自IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>而不仅仅是IdentityDbContext<ApplicationUser>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM