DbContext的第三方实现,如何同时实现IdentityDbContext

Third-party implementation of DbContext, how to also implement IdentityDbContext

我正在与 Audit.NET, an open-source auditing framework, which provides an extension for Entity Framework and DbContext here: AuditDbContext.cs

合作

// Implements DbContext public abstract partial class AuditDbContext : DbContext

我想使用这个 Entity Framework 扩展在我的项目中实现 Audit.NET 因为它会自动执行很多我需要手动执行的步骤(我能够手动使用 Audit.NET 并且不使用 Entity Framework 扩展名)。我 运行 遇到的问题是我的解决方案存储库实现了 IdentityDbContext,这当然也是 DbContext 的实现。

// Implements IdentityDbContext public class MyDataContext : IdentityDbContext<ApplicationUser> { public MyDataContext() : base("DefaultConnection") { } ...

没有现有的 AuditDbContext 实现 IdentityDbContext

我似乎想不出一种干净的方法来将这两者混合在一起并使我的存储库使用 AuditDbContext,特别是考虑到 AuditDbContext 具有受保护的构造函数,并且 [=10] =] 和 IdentityDbContextprotected 方法。我试图创建一个名为 AuditIdentityDbContext 的组合,其中包含每个上下文的 private 个副本,但我无法通过这样做来实现它们的所有接口。

由于 protected 成员,上面的所有 3 DbContext 类型似乎都需要继承。在我的职业生涯中,我第一次觉得多重继承在这种情况下实际上可能有所帮助,但鉴于这不可能,最好的选择是什么?

我唯一能想到的是创建一个新的 class 继承自 AuditDbContextIdentityDbContext<TUser> 并手动实现剩下的任何内容以匹配另一个的功能。虽然没有接口 classes 可以实现,所以我很确定这是行不通的。我觉得我一定是忽略了什么。

我曾在一个项目中遇到过类似的问题。我们最终做的是做两个单独的 DbContext 但是它们共享同一个数据库。

看看这个答案,它向您展示了如何访问 UsersRoles 属性,以及如何从IdentityDbContext

Why is Asp.Net Identity IdentityDbContext a Black-Box?

哎哟!

但你还是可以找到出路的。 IdentityDbContext没什么特别的,连界面都没有!所以您需要做的就是告诉您自己的 DbContext 有关用户和角色的信息并添加一些验证。

IdentityDbContext的肉是这个:

    public virtual IDbSet<TUser> Users { get; set; }

    public virtual IDbSet<TRole> Roles { get; set; }

    public bool RequireUniqueEmail { get; set; } // this might not be important for you

用您自己的 ApplicationUserApplicationRole 替换通用参数(这两个 类 都可以继承自 Identity-provided 类)

并添加或混合您自己的方法:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        if (modelBuilder == null)
        {
            throw new ArgumentNullException("modelBuilder");
        }

        // Needed to ensure subclasses share the same table
        var user = modelBuilder.Entity<TUser>()
            .ToTable("AspNetUsers");
        user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
        user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
        user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
        user.Property(u => u.UserName)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("UserNameIndex") { IsUnique = true }));

        // CONSIDER: u.Email is Required if set on options?
        user.Property(u => u.Email).HasMaxLength(256);

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

        modelBuilder.Entity<TUserLogin>()
            .HasKey(l => new { l.LoginProvider, l.ProviderKey, l.UserId })
            .ToTable("AspNetUserLogins");

        modelBuilder.Entity<TUserClaim>()
            .ToTable("AspNetUserClaims");

        var role = modelBuilder.Entity<TRole>()
            .ToTable("AspNetRoles");
        role.Property(r => r.Name)
            .IsRequired()
            .HasMaxLength(256)
            .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("RoleNameIndex") { IsUnique = true }));
        role.HasMany(r => r.Users).WithRequired().HasForeignKey(ur => ur.RoleId);
    }

    protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry,
        IDictionary<object, object> items)
    {
        if (entityEntry != null && entityEntry.State == EntityState.Added)
        {
            var errors = new List<DbValidationError>();
            var user = entityEntry.Entity as TUser;
            //check for uniqueness of user name and email
            if (user != null)
            {
                if (Users.Any(u => String.Equals(u.UserName, user.UserName)))
                {
                    errors.Add(new DbValidationError("User",
                        String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateUserName, user.UserName)));
                }
                if (RequireUniqueEmail && Users.Any(u => String.Equals(u.Email, user.Email)))
                {
                    errors.Add(new DbValidationError("User",
                        String.Format(CultureInfo.CurrentCulture, IdentityResources.DuplicateEmail, user.Email)));
                }
            }
            else
            {
                var role = entityEntry.Entity as TRole;
                //check for uniqueness of role name
                if (role != null && Roles.Any(r => String.Equals(r.Name, role.Name)))
                {
                    errors.Add(new DbValidationError("Role",
                        String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleAlreadyExists, role.Name)));
                }
            }
            if (errors.Any())
            {
                return new DbEntityValidationResult(entityEntry, errors);
            }
        }
        return base.ValidateEntity(entityEntry, items);
    }

(taken from source code as is)

当您需要创建 ApplicationUserManager 时需要 IUserStore 并且用户存储的默认实现需要 DbContext 因此您可以提供自己的 DbContext 实际上继承自 AuditDbContext

不用 double-inherit 就可以解决问题。万岁 open-source!

@trailmax 解决方案应该有效,但我更新了 Audit.NET 库以包括对 IdentityDbContext.

的支持

提供了一个新的 class AuditIdentityDbContext,因此您可以更改上下文以继承该上下文。

您应该将代码更改为:

public class MyDataContext : AuditIdentityDbContext<ApplicationUser>
{
    ...
}

注意:我是图书馆的主人

GitHub Issue