2

我正在构建一个多租户应用程序。出于这个原因,我添加了一个名为 OwnerId(角色的所有者)的列。我在使用 ASP.NET Identity 向 AspNetRoles 表添加角色时遇到了一些麻烦。

我有一个自定义 RoleValidator,每次都评估为 Success。但是,SaveChangesAsync 失败并显示:角色已存在。

我已经更改了 AspNetRoles RoleNameIndex 以适应 Name 和 OwnerId,但是 SaveChangesAsync 仍然失败。

我查看了这个线程,但已经实现了这些更正: IdentityRole in multi-tenant application

ApplicationRoleStore.cs

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using MyApp.Models.Identity.Context;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Threading.Tasks;
using System.Web;

namespace MyApp.Models.Identity
{
    public class ApplicationRoleStore : RoleStore<ApplicationRole, string, ApplicationUserRole>
    {
        private EntityStore<ApplicationRole> _roleStore;

        public ApplicationRoleStore(ApplicationDbContext context)
            : base(context)
        {
            _roleStore = new EntityStore<ApplicationRole>(context);
        }



        public IQueryable<ApplicationRole> GetStandardRoles()
        {
            var roles = Roles.AsNoTracking().Where(x => x.IsAdminRole == false && x.BaseRoleId == "");
            return (roles);
        }

        public IQueryable<ApplicationRole> GetTenantRoles(string ownerId)
        {
            var roles = Roles.AsNoTracking().Where(x => x.OwnerId == ownerId);
            return (roles);
        }

        public override async Task CreateAsync(ApplicationRole role)
        {
            if (role == null)
            {
                throw new ArgumentNullException("role");
            }

            if (String.IsNullOrEmpty(role.Id))
            {
                role.Id = Guid.NewGuid().ToString().ToLower();
            }

            _roleStore.Create(role);

            try
            {
                await Context.SaveChangesAsync().WithCurrentCulture();
            }
            catch (DbEntityValidationException e)
            {
                EntityValidationErrors.DisplayErrors(e);
                throw;
            }
        }
    }
}

ApplicationRoleValidator.cs

using Microsoft.AspNet.Identity;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Resources;
using System.Threading.Tasks;
using System.Web;

namespace MyApp.Models.Identity
{
    public class ApplicationRoleValidator : RoleValidator<ApplicationRole, string>
    {
        public ApplicationRoleValidator(ApplicationRoleManager manager) : base(manager)
        {
            Manager = manager;
        }

        private ApplicationRoleManager Manager { get; set; }

        public override async Task<IdentityResult> ValidateAsync(ApplicationRole item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            var errors = new List<string>();
            await ValidateRoleName(item, errors).WithCurrentCulture();
            if (errors.Count > 0)
            {
                return IdentityResult.Failed(errors.ToArray());
            }
            return IdentityResult.Success;
        }

        private async Task ValidateRoleName(ApplicationRole role, List<string> errors)
        {
            if (string.IsNullOrWhiteSpace(role.Name))
            {
                errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Name"));
            }
            else
            {
                var owner = Manager.FindByNameAndOwner(role.Name, role.OwnerId);
                if (owner != null && !EqualityComparer<string>.Default.Equals(owner.Id, role.Id))
                {
                    errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, role.Name));
                }
            }
        }
    }
}

应用程序角色.cs

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.Threading.Tasks;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations.Schema;

namespace MyApp.Models.Identity
{
    public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
    {
        public ApplicationRole() : base()
        {
        }

        public ApplicationRole(string name) : this()
        {
            base.Name = name;
        }

        public ApplicationRole(string name, string ownerId) : this(name)
        {
            OwnerId = ownerId;
        }

        public ApplicationRole(string name, string description, string ownerId) : this(name, "", description, ownerId)
        {
        }

        public ApplicationRole(string name, string addrv, string description, string ownerId, string baseRoleId = "") : this(name, ownerId)
        {
            Abbrv = addrv;
            Description = description;
            BaseRoleId = baseRoleId;
        }

        public string Abbrv { get; set; }
        public string Description { get; set; }

        public int Order { get; set; } = 0;
        public bool IsAdminRole { get; set; } = false;

        public string OwnerId { get; set; }
        [ForeignKey("OwnerId")]
        public virtual ApplicationUser Owner { get; set; }

        public string BaseRoleId { get; set; }
        [ForeignKey("BaseRoleId")]
        public virtual ApplicationRole BaseRole { get; set; }

    }
}

ApplicationDbContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using MyApp.Models.Identity;
using System.Threading.Tasks;
using System.Data.Entity;
using System.Security.Claims;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;

namespace MyApp.Models.Identity.Context
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, ApplicationUserRole, IdentityUserClaim>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
            base.Configuration.ProxyCreationEnabled = false;
        }

        static ApplicationDbContext()
        {
            // Set the database intializer which is run once during application start
            // This seeds the database with admin user credentials and admin role
            //Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
        }

        public static ApplicationDbContext Create()
        {
            ApplicationDbContext context = new ApplicationDbContext();
            context.Database.Initialize(true);
            return context;
        }

        //public virtual DbSet<ApplicationUser> Users { get; set; }
        //public virtual DbSet<ApplicationRole> Roles { get; set; }
        public virtual DbSet<UserDetail> UserDetails { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            //modelBuilder.Entity<ApplicationUser>().HasMany<ApplicationUserRole>((ApplicationUser u) => u.Roles);

            modelBuilder.Entity<ApplicationUser>()
                          .ToTable("AspNetUsers");

            var role = modelBuilder.Entity<ApplicationRole>()
                          .ToTable("AspNetRoles");
            role.Property(r => r.Name)
                .IsRequired()
                .HasMaxLength(256)
                .HasColumnAnnotation("Index", new IndexAnnotation(
                    new IndexAttribute("RoleNameIndex", 1)
                    { IsUnique = true }));

            role.Property(r => r.OwnerId)
                .IsRequired()
                .HasMaxLength(128)
                .HasColumnAnnotation("Index", new IndexAnnotation(
                    new IndexAttribute("RoleNameIndex", 2)
                    { IsUnique = true }));

            modelBuilder.Entity<ApplicationUserRole>().HasKey((ApplicationUserRole r) =>
                new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("AspNetUserRoles");
        }
    }
}
4

1 回答 1

2

我的解决方案是添加 ApplicationDbContext:

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
    {
        var res =  base.ValidateEntity(entityEntry, items);
        //hack to convince EF that AspNetRole.Name does not need to be unique
        if (!res.IsValid
            && entityEntry.Entity is ApplicationRole
            && entityEntry.State == EntityState.Added
            && res.ValidationErrors.Count == 1
            && res.ValidationErrors.First().PropertyName == "Role")
        {
            return new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
        }

        return res;

    }
于 2018-05-09T12:42:37.010 回答