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

时间:2017-02-10 20:20:22

标签: c# entity-framework asp.net-identity dbcontext audit.net

我正在与Audit.NET合作,这是一个开源审核框架,它为实体框架和DbContext提供了扩展:AuditDbContext.cs

// Implements DbContext public abstract partial class AuditDbContext : DbContext

我想在我的项目中使用此Entity Framework扩展实现Audit.NET,因为它会自动执行我需要手动执行的许多步骤(我可以手动使用Audit.NET实体框架扩展)。我遇到的问题是我的解决方案存储库实现了IdentityDbContext,当然这也是DbContext的实现。

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

没有现有AuditDbContext实施IdentityDbContext

我似乎无法想出一种干净的方式将这两者混合在一起并使我的存储库使用AuditDbContext,特别是考虑到AuditDbContext具有受保护的构造函数,并且DbContextIdentityDbContextprotected个方法。我尝试创建一个名为AuditIdentityDbContext的合成,其中每个上下文都有private个副本,但我无法通过这样做来完成所有接口。

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

我唯一能想到的是创建一个继承自AuditDbContextIdentityDbContext<TUser>的新类,并手动实现剩下的任何内容以匹配另一个的功能。虽然没有可以实现的接口类,所以我很确定这不会起作用。我觉得我必须忽略一些东西。

3 个答案:

答案 0 :(得分:3)

哎哟!

但你仍然可以找到出路。 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提供的类继承)

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

    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,因此您可以提供实际从AuditDbContext <继承的DbContext / p>

这应该可以做到,而不必双重继承。 Viva开源!

答案 1 :(得分:1)

我遇到过与我合作过的项目类似的问题。我们最终做的是做两个单独的DbContext但是它们共享相同的数据库。

查看此答案,了解如何访问UsersRoles属性,以及ClaimsLoginsUserRoles来自{{ 1}}

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

答案 2 :(得分:1)

@trailmax解决方案应该有效,但我已更新Audit.NET库以包含对%f = 101.000000 %.f = 101 %.02f = 101.00 的支持。

我们提供了一个新类AuditIdentityDbContext,您可以将上下文更改为继承该类。

您应该将代码更改为:

IdentityDbContext

注意:我是图书馆老板

GitHub Issue