简体   繁体   English

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

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

I'm working with Audit.NET , an open-source auditing framework, which provides an extension for Entity Framework and DbContext here: AuditDbContext.cs 我正在使用Audit.NET ,一个开源审计框架,它为Entity Framework和DbContext提供了一个扩展: AuditDbContext.cs

// Implements DbContext public abstract partial class AuditDbContext : DbContext

I'd like to implement Audit.NET in my project using this Entity Framework extension because it would automate a lot of the steps I'd otherwise need to do manually (I'm able to use Audit.NET manually and without the Entity Framework extension). 我想使用这个Entity Framework扩展在我的项目中实现Audit.NET,因为它会自动执行我需要手动执行的许多步骤(我可以手动使用Audit.NET而不使用Entity Framework延期)。 The problem I'm running into is that my solution repository implements IdentityDbContext which of course is an implementation of DbContext also. DbContext的问题是我的解决方案存储库实现了IdentityDbContext ,当然这也是DbContext一个实现。

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

There is no existing AuditDbContext that implements IdentityDbContext . 没有实现IdentityDbContext现有AuditDbContext

I can't seem to think of a clean way to mix these two together and make my repository use AuditDbContext , especially given that AuditDbContext has protected constructors, and that both DbContext and IdentityDbContext have protected methods. 我似乎无法想到将这两者混合在一起并使我的存储库使用AuditDbContext ,特别是考虑到AuditDbContext具有受保护的构造函数,并且DbContextIdentityDbContext都具有protected方法。 I attempted to create a composition called AuditIdentityDbContext that had private copies of each context, but I'm not able to fulfill all of their interfaces by doing so. 我试图创建一个名为AuditIdentityDbContext ,它包含每个上下文的private副本,但是我无法通过这样做来完成所有接口。

It seems like all 3 DbContext types above need to be inherited from because of the protected members. 看起来上面的所有3个DbContext类型都需要继承,因为protected成员。 For the first time in my career, I feel like multiple inheritance might actually help in this situation, but given that isn't a possibility, what would be the best alternative? 在我的职业生涯中,我觉得多次继承可能在这种情况下实际上有所帮助,但鉴于这不可能,最好的替代方案是什么?

The only thing I can think of is creating a new class that inherits from either AuditDbContext or IdentityDbContext<TUser> and manually implement whatever is left to match the functionality of the other. 我唯一能想到的是创建一个继承自AuditDbContextIdentityDbContext<TUser>的新类,并手动实现剩下的任何内容以匹配另一个的功能。 There are no interface classes to implement though, so I'm pretty sure this isn't going to work. 虽然没有可以实现的接口类,所以我很确定这不会起作用。 I feel like I must be overlooking something. 我觉得我必须忽略一些东西。

Ouch! 哎哟!

But you can still find a way out. 但你仍然可以找到出路。 There is nothing special about IdentityDbContext , there is even no interface there! IdentityDbContext没有什么特别之处,甚至没有接口! So all you need to do is to tell your own DbContext about users and roles and add some validation. 所以你需要做的就是告诉你自己的DbContext关于用户和角色并添加一些验证。

The meat of IdentityDbContext is this: 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

Replace generic parameters with your own ApplicationUser and ApplicationRole (both of these classes can inherit from Identity-provided classes) 用您自己的ApplicationUserApplicationRole替换泛型参数(这两个类都可以从Identity提供的类继承)

And add or blend with your own methods: 并添加或混合您自己的方法:

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

And

    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) (源自原始代码)

And when you need to create ApplicationUserManager it takes IUserStore and default implementation of user store takes DbContext so you can provide your own DbContext that actually inherit from AuditDbContext 当你需要创建ApplicationUserManager它需要IUserStore并且用户存储的默认实现需要DbContext因此你可以提供你自己的DbContext,它实际上是从AuditDbContext继承的

This should do the trick without having to double-inherit. 这应该可以做到这一点,而不必双重继承。 Viva open-source! 万岁开源!

I had a similar problem with a project that I had worked on. 我在一个项目中遇到了类似的问题。 What we ended up doing was doing two separate DbContext however they share the same database. 我们最终做的是做两个单独的DbContext但是它们共享相同的数据库。

Take a look at this answer that shows you how to access Users and Roles properties, and Claims , Logins and UserRoles from the IdentityDbContext 看一下这个答案,它向您展示如何从IdentityDbContext访问UsersRoles属性,以及ClaimsLoginsUserRoles

Why is Asp.Net Identity IdentityDbContext a Black-Box? 为什么Asp.Net Identity IdentityDbContext是黑盒子?

@trailmax solution should work, but I've updated the Audit.NET library to include support for IdentityDbContext . @trailmax解决方案应该可以工作,但我更新了Audit.NET库以包含对IdentityDbContext支持。

A new class AuditIdentityDbContext is provided so you can change your context to inherit from that one. 提供了一个新类AuditIdentityDbContext ,以便您可以将上下文更改为从该上下文继承。

You should change your code to: 您应该将代码更改为:

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

Note: I'm the library owner 注意:我是图书馆老板

GitHub Issue GitHub问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM