簡體   English   中英

第三方實現DbContext,如何實現IdentityDbContext

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

我正在使用Audit.NET ,一個開源審計框架,它為Entity Framework和DbContext提供了一個擴展: AuditDbContext.cs

// Implements DbContext public abstract partial class AuditDbContext : DbContext

我想使用這個Entity Framework擴展在我的項目中實現Audit.NET,因為它會自動執行我需要手動執行的許多步驟(我可以手動使用Audit.NET而不使用Entity Framework延期)。 DbContext的問題是我的解決方案存儲庫實現了IdentityDbContext ,當然這也是DbContext一個實現。

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

沒有實現IdentityDbContext現有AuditDbContext

我似乎無法想到將這兩者混合在一起並使我的存儲庫使用AuditDbContext ,特別是考慮到AuditDbContext具有受保護的構造函數,並且DbContextIdentityDbContext都具有protected方法。 我試圖創建一個名為AuditIdentityDbContext ,它包含每個上下文的private副本,但是我無法通過這樣做來完成所有接口。

看起來上面的所有3個DbContext類型都需要繼承,因為protected成員。 在我的職業生涯中,我覺得多次繼承可能在這種情況下實際上有所幫助,但鑒於這不可能,最好的替代方案是什么?

我唯一能想到的是創建一個繼承自AuditDbContextIdentityDbContext<TUser>的新類,並手動實現剩下的任何內容以匹配另一個的功能。 雖然沒有可以實現的接口類,所以我很確定這不會起作用。 我覺得我必須忽略一些東西。

哎喲!

但你仍然可以找到出路。 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);
    }

(源自原始代碼)

當你需要創建ApplicationUserManager它需要IUserStore並且用戶存儲的默認實現需要DbContext因此你可以提供你自己的DbContext,它實際上是從AuditDbContext繼承的

這應該可以做到這一點,而不必雙重繼承。 萬歲開源!

我在一個項目中遇到了類似的問題。 我們最終做的是做兩個單獨的DbContext但是它們共享相同的數據庫。

看一下這個答案,它向您展示如何從IdentityDbContext訪問UsersRoles屬性,以及ClaimsLoginsUserRoles

為什么Asp.Net Identity IdentityDbContext是黑盒子?

@trailmax解決方案應該可以工作,但我更新了Audit.NET庫以包含對IdentityDbContext支持。

提供了一個新類AuditIdentityDbContext ,以便您可以將上下文更改為從該上下文繼承。

您應該將代碼更改為:

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

注意:我是圖書館老板

GitHub問題

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM