EF 6中使用接口作为外键导航属性关联ApplicationUser的实现问题
EF 6中使用接口作为外键导航属性关联ApplicationUser的实现问题
我之前也碰到过类似的困境,EF6确实不支持将接口作为导航属性的目标类型,因为EF需要明确的实体类(而非抽象的接口)来构建数据库映射关系、生成动态代理,这就是你看到那个InvalidOperationException的根本原因。
针对你的场景,我推荐用「基础实体类+继承」的方案来解决,既能保持Model项目和Application项目的解耦,又能让EF正常识别导航关系:
步骤1:在Model项目中定义基础用户抽象类
把你原本IAppUser接口里的公共属性,放到一个抽象的BaseUser类中(EF能识别抽象类作为实体基类):
public abstract class BaseUser { // 对应IdentityUser的主键 public string Id { get; set; } // 这里放你需要共享的用户属性,比如: public string FirstName { get; set; } public string LastName { get; set; } }
步骤2:让ApplicationUser继承BaseUser
在Application项目中,修改ApplicationUser使其同时继承IdentityUser和BaseUser:
public class ApplicationUser : IdentityUser, BaseUser { // 这里保留你的自定义属性 public int? CustomerId { get; set; } }
步骤3:修改EntityA的导航属性类型
把EntityA里的IAppUser类型导航属性换成BaseUser:
public class EntityA { [Key] public int EntityId { get; set; } public DateTime? CreateDate { get; set; } [StringLength(128)] [ForeignKey("CreateByUser")] public string CreateByUserId { get; set; } // 把IAppUser改成BaseUser public virtual BaseUser CreateByUser { get; set; } // ...其他属性 }
步骤4:在DbContext的OnModelCreating中配置关系
现在EF能正确识别BaseUser对应的具体实体是ApplicationUser,你可以这样配置关联:
protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // 先保留你原本的Identity表名映射配置 modelBuilder.Entity<ApplicationUser>().ToTable("User"); modelBuilder.Entity<IdentityRole>().ToTable("Role"); modelBuilder.Entity<IdentityUserRole>().ToTable("UserRole"); modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaim"); modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogin"); // 配置EntityA和ApplicationUser的关联 modelBuilder.Entity<EntityA>() .HasOptional(a => (ApplicationUser)a.CreateByUser) .WithMany() // 如果不需要反向导航就保持空 .HasForeignKey(a => a.CreateByUserId) .WillCascadeOnDelete(false); }
补充说明
如果你不想在Model项目中引入Identity相关的依赖,也可以把BaseUser做成一个无依赖的纯实体类,只包含你需要共享的属性,EF依然能正常处理这种继承关系的映射。
另外,你之前尝试用接口的思路本身没问题,但EF6的实体映射系统不支持接口作为导航属性的目标,这是框架的限制,换成抽象基类就能绕过这个问题啦。
备注:内容来源于stack exchange,提问作者orenb
相关产品推荐
相关产品推荐

