[英]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
具有受保护的构造函数,并且DbContext
和IdentityDbContext
都具有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. 我唯一能想到的是创建一个继承自
AuditDbContext
或IdentityDbContext<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) 用您自己的
ApplicationUser
和ApplicationRole
替换泛型参数(这两个类都可以从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
访问Users
和Roles
属性,以及Claims
, Logins
和UserRoles
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 注意:我是图书馆老板
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.