实体框架中的多态关联 [英] Polymorphic Associations in Entity Framework

查看:135
本文介绍了实体框架中的多态关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个遗留数据库,其中有几个表是使用多形态关联。通过多态关联,我的意思是根据列 ObjectType ,这些表可以是不同表的子对象。



示例:




  • 文档表具有 DocumentID (标识主键),其他一些列和2个特殊列,名为 ObjectType 的ObjectID

  • 如果 ObjectType ='STUDENT' ObjectID 指向学生表。

  • 如果 ObjectType ='TEACHER'​​ ObjectID 指向教师表等



这与这种设计(如没有外键方法)或这一个(描述为反模式)。显然,这些列上的没有外键约束(AFAIK没有数据库将允许这种关系)



我正在使用实体框架6(使用Fluent API进行代码优先)编写一个新的数据访问层,它应与现有代码并行工作。由于此数据库结构已部署到数百个不同的客户(每个客户不同的代码库和不同的数据库自定义),因此修改现有表的结构是不可选的。



我的问题是:如何将这些多态关联映射到我的EF代码优先模型?






编辑:似乎我正在设计错误实体上的类层次结构。我的理解是,我有很多GenericChildTables(如Documents),它应该指向一个具有复合键ObjectType + ObjectID的(不存在的)实体。然后我试图创建这个新实体(让我们称之为BusinessObject),并将我的核心实体(学生,教师等)映射为此BusinessObject的子类型。



设计可能只是一个简单的错误,也许是完全不可能的,因为我正在创建的这个新表(BusinessObject)取决于StudentID / TeacherID等,不是这些表的父母。使用一些丑陋的解决方法,我可以将BusinessObject创建为每个核心实体的单个子对象,并将这些BusinessObjects映射到多态表,并且其工作正常但是完全错误。



然后我看到 Gert Ardold的问题,并意识到应该设计为一个类层次结构不是学生/教师/ etc(分组为一个通用实体),而是每个ChildTables,其中根据 ObjectType 鉴别符持有不同的子类型 - 这些子类型应该被分解成子类型。

解决方案

看来我正在设计类层次结构错误的实体。我的理解是,我有很多GenericChildTables(如Documents),它应该指向一个具有复合键ObjectType + ObjectID的(不存在的)实体。然后我试图创建这个新实体(让我们称之为BusinessObject),并将我的核心实体(学生,教师等)映射为此BusinessObject的子类型。



然后我看到 Gert Ardold的问题,并意识到正确的继承设计不是将学生/教师/ etc分组成一个超类型,而是将这些GenericChildTables分成多个子类型。



我将使用Documents表格作为示例来显示如何将GenericChildTables转换为 TPH ,以及我如何映射我的核心实体(学生,教师等)到这些子类型的集合。



首先,我创建了派生类(子类型),添加了导航属性,并将这些子类型映射到基本类型使用 ObjectType 作为类型鉴别器:

  public class StudentDocument:Document 
{
public Student Student {get;组; }
public int StudentID {get;组;
}
public class TeacherDocument:Document
{
public Teacher Teacher {get;组; }
public int TeacherID {get;组;
}
modelBuilder.Entity< Document>()
.Map< StudentDocument>(m => {
m.Requires(ObjectType)。HasValue(STUDENT );
})
.Map< TeacherDocument>(m => {
m.Requires(ObjectType)。HasValue(TEACHER);
}) ;

然后我将导航属性添加到我的核心类(学生和教师),指向创建的子类型:

  partial class Student 
{
public virtual ICollection< StudentDocument>文件{get;组; }
}
partial class教师
{
public virtual ICollection< TeacherDocument>文件{get;组;
}

我创建了关系Student.Documents和Teacher.Documents的关系映射。请注意,我使用属性StudentID和TeacherID,但它们被物理映射到ObjectID列:

  var sl = modelBuilder.Entity< StudentDocument>(); 
sl.Property(t => t.StudentID).HasColumnName(ObjectID);
sl.HasRequired(t => t.Student).WithMany(t => t.Documents).HasForeignKey(d => d.StudentID);

var al = modelBuilder.Entity< TeacherDocument>();
al.Property(t => t.TeacherID).HasColumnName(ObjectID); (b => t.Teacher)。

最后,我从基本类型(Document)中删除属性 ObjectType ,因为它是一种类型鉴别器,只能在内部使用(不能在课堂上公开)。

我也 strong>从基本类型 ObjectID ,因为这应该只映射在子类型(分别映射为StudentID和TeacherID)。



所有的工作都像一个魅力!



PS:请注意,您正在使用T4模板(代码从数据库开始),它们将始终重新生成这些属性,因为模板不了解hiearch,所以他们将文档映射到具有每列的属性的单个实体,因此您应该手动排除这些属性。 >

I have a legacy database that has a few tables which were designed using Polymorphic Associations. By polymorphic associations, I mean that those tables can be child of different tables according to a column ObjectType.

Example:

  • Documents table has DocumentID (identity primary key), some other columns, and 2 special columns called ObjectType and ObjectID.
  • If ObjectType='STUDENT', ObjectID points to Students table.
  • If ObjectType='TEACHER', ObjectID points to Teachers table, etc.

This is similar to this design (as described in the "no foreign key approach") or this one (described as an anti-pattern). And obviously, there are no foreign key constraints on those columns (AFAIK no database would allow that kind of relationship).

I'm writing a new Data Access Layer using Entity Framework 6 (code-first with Fluent API) which should work side-by-side with existing code. Since this database structure was deployed to hundreds of different customers (each one with a different codebase and different database customizations), modifying the structure of existing tables is not an option.

My question is: How do I map those Polymorphic Associations into my EF code-first model?


EDIT: It seems that I was trying to design the class hierarchy on the wrong entities. My understanding was that I had a lot of "GenericChildTables" (like Documents) that should point to a (non-existing) entity that would have a composite key ObjectType+ObjectID. And then I was trying to create that new entity (let's call it "BusinessObject") and map my core entities (Students, Teachers, etc) to be subtypes of this BusinessObject.

THAT design was probably just plain wrong, and maybe totally impossible because this new table that I was creating (BusinessObject) depended on StudentID/TeacherID, etc, so it couldn't be a parent of those tables. Using some ugly workarounds I could create that BusinessObject as a single-child for each core entity, and map those BusinessObjects to the polymorphic tables, and it was working indeed but in a completely wrong design.

Then I saw Gert Ardold's question and realized that what should be designed as a class hierarchy was NOT Students/Teachers/etc (grouped into a generic entity), but each one of those ChildTables, which were holding different subtypes according to the ObjectType discriminator - those were the types that should be splitted into subtypes. See my solution on my own answer below.

解决方案

It seems that I was trying to design the class hierarchy on the wrong entities. My understanding was that I had a lot of "GenericChildTables" (like Documents) that should point to a (non-existing) entity that would have a composite key ObjectType+ObjectID. And then I was trying to create that new entity (let's call it "BusinessObject") and map my core entities (Students, Teachers, etc) to be subtypes of this BusinessObject.

Then I saw Gert Ardold's question and realized that the correct inheritance design was NOT about grouping Students/Teachers/etc into a supertype, but about splitting those GenericChildTables into multiple subtypes.

I'll use the Documents table as an example to show how I converted those GenericChildTables into a TPH, and how I mapped my core entities (Students, Teachers, etc) to collections of those subtypes.

First, I created the derived classes (subtypes), added navigation properties, and mapped those subtypes to the base type using ObjectType as type discriminator:

public class StudentDocument : Document
{
    public Student Student { get; set; }
    public int StudentID { get; set; } 
}
public class TeacherDocument : Document
{
    public Teacher Teacher { get; set; }
    public int TeacherID { get; set; } 
}
modelBuilder.Entity<Document>()
.Map<StudentDocument>(m => {
    m.Requires("ObjectType").HasValue("STUDENT");
})
.Map<TeacherDocument>(m => {
    m.Requires("ObjectType").HasValue("TEACHER");
});

Then I added the navigation properties to my core classes (Student and Teachers), pointing to the subtypes created:

partial class Student
{
   public virtual ICollection<StudentDocument> Documents { get; set; }
}
partial class Teacher
{
   public virtual ICollection<TeacherDocument> Documents { get; set; }
}

I created the mappings for relationships Student.Documents and Teacher.Documents. Please note that I use properties StudentID and TeacherID, but they are phisically mapped to ObjectID column:

var sl = modelBuilder.Entity<StudentDocument>();
sl.Property(t => t.StudentID).HasColumnName("ObjectID");
sl.HasRequired(t => t.Student).WithMany(t => t.Documents).HasForeignKey(d => d.StudentID);

var al = modelBuilder.Entity<TeacherDocument>();
al.Property(t => t.TeacherID).HasColumnName("ObjectID");
al.HasRequired(t => t.Teacher).WithMany(t => t.Documents).HasForeignKey(d => d.TeacherID);

Finally, I removed from the base type (Document) the property ObjectType, because it is a type discriminator, and should be used only internally (cannot be exposed on class).
I also removed from the base type ObjectID because this should be mapped only on the subtypes (mapped respectively as StudentID and TeacherID).

And everything worked like a charm!

PS: Please note that if you are using T4 templates (code first from database) they will always regenerate those properties, because templates know nothing about hiearchies, so they map Documents into a single entity with properties for every column, so you should manually exclude those properties.

这篇关于实体框架中的多态关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆