多租户应用程序中的IdentityRole [英] IdentityRole in multi-tenant application

查看:101
本文介绍了多租户应用程序中的IdentityRole的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在构建一个ASP.NET MVC 5多租户解决方案,在角色方面有一个小问题.我创建了一个自定义角色实体,如下所示:

I am building an ASP.NET MVC 5 multi-tenant solution and have a slight problem when it comes to roles. 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:

名称管理员已被使用.

很显然,存在一些潜在的检查,以检查角色名称在ASP.NET Identity中某个地方是否唯一.有什么方法可以更改此设置,以便我可以通过"TenantId +名称"(而不是仅名称)来寻找唯一性吗?

Obviously there is some underlying check for role names to be unique in ASP.NET Identity somewhere. Is there some way to change this so that I can make it look for uniqueness by "TenantId + Name", instead of Name only?

更新

使用dotPeek反编译DLL,我发现我需要创建自己的IIdentityValidator实现,当然还要修改RoleManager.所以,这是我的角色验证器:

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

还有我的角色经理:

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:

无法在具有唯一索引"RoleNameIndex"的对象"dbo.AspNetRoles"中插入重复的键行.重复的键值为(管理员). 该声明已终止

Cannot insert duplicate key row in object 'dbo.AspNetRoles' with unique index 'RoleNameIndex'. The duplicate key value is (Administrators). The statement has been terminated

我可以修改数据库以更改我假定的索引,但是我需要在安装时使其正确,因为我正在构建的解决方案是CMS,将来将用于许多安装...

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...

我的第一个想法是我需要以某种方式修改ApplicationRole实体的EntityTypeConfiguration<T>.但是我当然不能立即访问它...它只是由ApplicationDbContext自动创建的,因为它是从IdentityDbContext<ApplicationUser>继承的.我将不得不更深入地研究反汇编的代码,看看我能找到什么...

My first thought is I somehow need to modify the EntityTypeConfiguration<T> for the ApplicationRole entity. 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>. I will have to delve deeper into the disassembled code and see what I can find...

更新2

好的,我正在使用base.OnModelCreating(modelBuilder);来获取身份成员资格表的配置.我删除了该行,并将反编译的代码复制到了我的OnModelCreating方法中,但是删除了用于创建索引的部分.这(并删除了数据库中的索引)解决了我以前遇到的错误..但是,我又遇到了1个错误,现在我完全迷住了...

OK, I was using 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. 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...

我收到如下错误消息:

无法将值NULL插入表"dbo.AspNetRoles"的名称"列中;列不允许为空. INSERT失败. 该声明已终止.

Cannot insert the value NULL into column 'Name', table 'dbo.AspNetRoles'; column does not allow nulls. INSERT fails. The statement has been terminated.

这没有任何意义,因为在调试时,我可以清楚地看到我正在以要创建的角色传递Name和TenantId.这是我的代码:

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. This is my code:

var result = await roleManager.CreateAsync(new ApplicationRole
            {
                TenantId = tenantId,
                Name = role.Name
            });

这些值不为null,所以我不知道这里发生了什么.任何帮助将不胜感激.

Those values are not null, so I don't know what's going on here anymore. Any help would be most appreciated.

更新3

我创建了自己的RoleStore,该角色继承自RoleStore<ApplicationRole>,并且覆盖了CreateAsync((ApplicationRole role)方法,因此我可以调试此部分并查看发生的情况.见下文:

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:

继续运行代码后,我仍然在死亡的黄屏上看到以下错误:

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.

更新4

好的,我现在更接近答案了.我从头开始创建了一个新的数据库(允许EF创建它),我注意到并没有创建Name列...仅创建了ID和TenantId.意味着先前的错误是因为我现有的数据库已经具有Name列,并且被设置为NOT NULL ..并且EF由于某种原因而忽略了我的角色实体的Name列,我认为这与从IdentityRole继承有关.

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.

这是我的模型配置:

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

我认为这可能与索引配置有关,所以我只删除了这两个(TenantId和Name)并替换为:

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:

rolesTable.Property(x => x.Name)
                .IsRequired()
                .HasMaxLength(256);

但是,名称"列仍未创建.现在和以前之间的唯一区别是,我使用的是modelBuilder.Entity<ApplicationRole>(),而我想默认的是modelBuilder.Entity<IdentityRole>() ...

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...

如何让EF识别基类的 Name 属性和派生类ApplicationRole TenantId 属性?

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?

推荐答案

好的,我已经解决了这个问题.答案是首先遵循我在原始帖子中添加的所有更新,然后最后要做的是使我的ApplicationDbContextIdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>继承而不仅仅是IdentityDbContext<ApplicationUser>

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>

这篇关于多租户应用程序中的IdentityRole的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆