实体框架动态字段

时间:2017-11-08 10:59:56

标签: entity-framework architecture

我正在使用EntityFramework(代码优先)设计数据输入应用程序来收集客户详细信息。

所需的数据结构很简单。

客户实体有一些扁平和一对多的详细信息(例如姓名,电话号码等),然后是大量的多对多属性,这些属性始终遵循相同的模式,允许多种选择一个列表(在UI中,这将显示为一个复选框列表),用户也可以添加项目。对于这些多选属性中的每一个,还有一个Notes属性,允许用户解释为什么这些详细信息与客户连接(换句话说,这只是Customer实体中的字符串)。

由于这些属性的相似性和数据的相对简单性,我开始寻求使用继承进行建模,但我现在认为可能有更好的方法来实现这一点,特别是因为如果system允许管理员用户动态添加此类型的新属性。

我正在寻找任何建议来实现这一点,而无需手动定义和连接所有实体,或至少最大限度地减少执行此操作所需的代码量。

1 个答案:

答案 0 :(得分:1)

SQL不知道继承的概念。但是,有几种策略可以让实体框架接受您继承的类。您应该使用哪种策略取决于您最常询问的查询类型。

假设您有两个班级StudentTeacher。这两个类都有很多共同的属性(可能还有方法)。您想将它们放在基类中:Person。您不希望能够创建Person对象,因此您的Person类将是抽象的。

在C#中:

abstract class Person
{
    ... // common person properties
}

class Teacher : Person
{
    public int Id {get; set;}    // Primary Key
    ... // Teacher properties
}

class Student : Person
{
    public int Id {get; set;}    // Primary Key
    ... // Student properties
}

您不打算仅PersonTeachers创建Students个对象。因此,您可以创建Teachers表和Students表。 Teachers表包含所有Teacher属性以及所有Person属性。同样,Student表包含Student属性和所有Person属性。对于每个具体(=非抽象)类,您都可以创建一个表。

此策略称为Table-Per-Concrete-Class (TPC)。它与组合非常相似:教师'拥有'人物属性,而不是继承人物属性。它遵循旧的adagium“Favour composition over inheritance

您可以在DbContext

中通知实体框架您希望采用此策略
class MyDbContext : DbContext
{
    public DbSet<Student> Students {get; set;}
    public DbSet<Teacher> Teachers {get; set;}
    // we don't want a Person table, so no DbSet<Person>

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Tell entity framework that the Teacher Table will include Person properties
        modelBuilder.Entity<Teacher>().Map(m =>
        {
            m.MapInheritedProperties();
        });

        // Tell entity framework that the Student table will include Person properties
        modelBuilder.Entity<Student>().Map(m =>
        {
            m.MapInheritedProperties();
        });            
    }
}

查询“给我所有教师......”或“给我这些学生......”将涉及一张桌子。但是,如果你问:“给我所有的人......”将要求连续两张桌子。

var result = myDbContext.Teachers.Cast<Person>()
    .Concat(myDbContext.Students.Cast<Person>())
    .Where(person => ... // something with Person properties)
    .Select(person => ... // something with Person properties);

每当我需要建模继承时,我大多数时候都会使用这种TPC策略。

如果您认为Persons {而非Teachers代替Person,那么请考虑使用Table Per Type (TPT)

在TPT中,您将拥有三个表:一个Person表,包含所有Teacher属性,一个Teacher表,其中包含Person属性和一个外键,此Teacher的{​​{1}}属性。类似地,您将拥有一个Student表,其中包含其继承的Person属性的外键。

要求“所有人......”只涉及一张桌子,无论该人是学生还是教师。因为你要求人,你不需要任何学生属性。

要求“所有教师......”将始终涉及两个表,即获取教师属性的教师表和访问人物属性的人员表。

因此,如果您经常询问“那些......的人”,那么对于“......的教师”,请考虑使用TPT。