IdentityRole no aplicativo multilocatário

Estou criando uma solução de multilocatário do ASP.NET MVC 5 e tenho um pequeno problema quando se trata de funções. Eu criei uma entidade de função personalizada da seguinte maneira:

public class ApplicationRole : IdentityRole, ITenantEntity
    {
        public ApplicationRole()
            : base()
        {
        }

        public ApplicationRole(string roleName)
            : base(roleName)
        {
        }

        public int? TenantId { get; set; }
    }

E feito tudo o mais necessário .. está tudo funcionando bem, exceto por uma coisa ...; quando um administrador de inquilino tenta adicionar uma nova função e se o nome dessa função já estiver sendo usado por uma função criada por outro inquilino, ele receberá o seguinte erro:

O nome Administradores já está em uso.

Obviamente, há alguma verificação subjacente de nomes de função serem exclusivos na identidade do ASP.NET em algum lugar. Existe alguma maneira de mudar isso para que eu possa procurar exclusividade por "TenantId + Name", em vez de apenas Nome?

ATUALIZAR

Usando o dotPeek para descompilar as DLLs, descobri que preciso criar minha própria implementação do IIdentityValidator e, é claro, modificar meu RoleManager. Então, aqui está o meu validador de função:

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

E meu gerente de função:

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

No entanto, agora estou recebendo um novo erro:

Não é possível inserir linha de chave duplicada no objeto 'dbo.AspNetRoles' com o índice exclusivo 'RoleNameIndex'. O valor da chave duplicada é (Administradores). A instrução foi encerrada

Eu poderia apenas modificar o db para alterar os índices, suponho, mas preciso que ele esteja correto na instalação porque a solução que estou construindo é um CMS e será usada para muitas instalações no futuro ...

Meu primeiro pensamento é que, de alguma forma, preciso modificar oEntityTypeConfiguration<T> para oApplicationRole entidade. Mas é claro que não tenho acesso imediato a isso ... ele é criado automaticamente peloApplicationDbContext porque herda deIdentityDbContext<ApplicationUser>. Vou ter que me aprofundar no código desmontado e ver o que posso encontrar ...

ATUALIZAÇÃO 2

OK, eu estava usandobase.OnModelCreating(modelBuilder); para obter as configurações para as tabelas de associação de identidade. Eu removi essa linha e copiei o código descompilado para o meuOnModelCreating método, mas removeu a parte para criar o índice. Isso (e removendo o índice no banco de dados) resolveu o erro que eu tinha antes .. no entanto, tenho mais 1 erro e estou totalmente perplexo agora ...

Recebo uma mensagem de erro da seguinte maneira:

Não é possível inserir o valor NULL na coluna 'Nome', tabela 'dbo.AspNetRoles'; A coluna não permite nulos. INSERT falha. A instrução foi encerrada.

Isso não faz sentido, porque ao depurar, posso ver claramente que estou passando o Nome e o TenantId na função que estou tentando criar. Este é o meu código:

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

Esses valores não são nulos, então não sei mais o que está acontecendo aqui. Qualquer ajuda seria muito apreciada.

ATUALIZAÇÃO 3

Criei meu próprio RoleStore, que herda deRoleStore<ApplicationRole> e eu substituí oCreateAsync((ApplicationRole role) método para que eu possa depurar esta parte e ver o que está acontecendo. Ver abaixo:

Depois de continuar executando o código, ainda recebo o seguinte erro na tela amarela da morte:

Alguém, qualquer pessoa, ajude a esclarecer o que está acontecendo aqui e se é possível corrigir isso.

ATUALIZAÇÃO 4

OK, estou mais perto da resposta agora. Criei um novo banco de dados do zero (permitindo que o EF o criasse) e notei que a coluna Nome não é criada ... apenas Id e TenantId .. isso significa o anterior O erro ocorre porque meu banco de dados existente já tinha a coluna Nome e foi definido como NOT NULL .. e o EF está ignorando a coluna Nome da minha entidade de função por algum motivo, que eu suponho ter algo a ver com ela herdar do IdentityRole.

Esta é a configuração do modelo que tenho:

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

Eu pensei que talvez fosse algo a ver com a configuração do índice, então eu apenas removi os dois (TenantId e Name) e substituí-o por este:

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

No entanto, a coluna Nome ainda não foi criada. A única diferença entre agora e antes é que estou usandomodelBuilder.Entity<ApplicationRole>() Considerando que o padrão teria sidomodelBuilder.Entity<IdentityRole>() Eu suponho...

Como faço para que a EF reconheça os doisNome propriedade da classe base,IdentityRole e aTenantId propriedade da classe derivadaApplicationRole?

questionAnswers(1)

yourAnswerToTheQuestion