实体框架中的派生类型

问题描述 投票:1回答:3

我有一个Person类和库存可以是两种类型:SalesCustomerService

SalesCustomerService有其独特的属性,Peron拥有共同的属性。我希望能够查询

那么,在创建所有三个类时,如何在它们之间创建EF关系?或者有更好的方法来考虑课程的划分吗?

我不想将Person作为抽象类,因为大多数时候我想查询公共属性。

c# entity-framework relationship entity-relationship
3个回答
1
投票

您可以在此处采取3种可能的方法:


1.将所有类型存储在单个表中(每个层次表)

您将拥有一个Person类,其中包含三个类之间所需的所有可能属性。此外,您还要添加PersonType枚举,以便为每个条目指定不同的类型。

public class Person
{
    public int PersonId { get; set; }

    public string Name { get; set; }

    // ...

    public PersonType Type { get; set; }
}

public enum PersonType
{
    Sales,
    CustomerService
}

这通常是最简单,性能最佳的方法。最大的问题是专业领域。由于每个类型都在这一个表中,因此该表将需要包含任何类型可能需要的所有字段。这也意味着所有专业字段都需要可以为空,这使得难以强制执行具有特定字段的特定类型。


2.将每种类型存放在单独的表中(每个混凝土类别的表)

你可以改为只使用PersonSales表来重复CustomerService表中包含的属性,而不是使用Person表。

public class Sales
{
    public int SalesId { get; set; }

    public string Name { get; set; }

    // ...
}

public class CustomerService
{
    public int CustomerServiceId { get; set; }

    public string Name { get set; }

    // ... 
}

当然,如果你愿意,你仍然可以在代码中利用Person抽象。使用代码优先,您可以使用继承:

public class Person
{
    public string Name { get; set; }
}

public class Sales : Person
{
    public int SalesId { get; set; }

    // ...
}

public class CustomerService : Person
{
    public int CustomerServiceId { get; set; }

    // ...
}

只需确保您只在Sales子类中为CustomerServiceDbContext定义实体:

public class MyContext : DbContext
{
    // Do not include a DbSet for Person.

    public DbSet<Sales> Sales { get; set; }

    public DbSet<CustomerService> CustomerService { get; set; }

    // ...
}

这种方法的优点是您的类型被分成清晰,不同的集合。缺点是没有简单的方法可以通过每个“人”进行通用搜索,因为就数据库而言,抽象不存在。例如,如果您想找到具有特定名称的人,则必须手动通过Sales表和CustomerService表进行单独搜索,这可能并不理想。此外,如果您最终选择了在销售和客户服务中担任职务的人员,您将创建冗余,因为您需要输入两个条目的信息。


3.将每种类型和基本类型存储在各自的表中(每种类型的表)

在你的Person类之上,你还将创建SalesCustomerService类,每个类都指定它们的专用属性并包含对Person类的引用。这是一个被称为composition over inheritance的常见原则;因为我们无法在数据库中有效地建模继承,所以我们可以使用组合代替。

public class Person
{
    public int PersonId { get; set; }

    public string Name { get; set; }

    // ...
}

public class Sales
{
    public int SalesId { get; set; }

    public int PersonId { get; set; }

    public virtual Person { get; set; }

    // ...
}

public class CustomerService
{
    public int CustomerServiceId { get; set; }

    public int PersonId { get; set; }

    public virtual Person { get; set; }

    // ...
}

这将允许您为每种类型添加专用属性,同时仍保留可以搜索的通用Person表。如果他们担任多个角色,您还可以重复使用此人的信息。缺点是创建一个新的SalesCustomerService记录有点单调乏味,因为你还需要找到现有的Person记录或创建一个新记录。这也可能不是最好的性能,因为查询可能最终需要连接。


您应采取的方法取决于您的需求。如果您想更深入地了解这三种策略,请查看本教程,以便在实体代码中实现继承:

http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx


1
投票

使用Entity Framework Core,您可以在数据库中使用继承:

public class PeopleContext : DbContext {
    public DbSet<Person> Persons { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder) {
        modelBuilder.Entity<CustomerService>().HasBaseType<Person>();
        modelBuilder.Entity<Sales>().HasBaseType<Person>();
    }
}

这将创建一个具有所有派生类型属性的表。此外,它将创建一个Discriminator-Column,以便在查询数据库时EF Core实例化正确的派生类型:

context.Users.Add(new Sales() {
    Id = 1
});

context.SaveChanges();

// This will actually be of type "Sales"
var salesPerson = context.Persons.Single(u => u.Id == 1);

有关更多信息,请查看herehere


0
投票

我更喜欢使用System.ComponentModel.DataAnnotations将属性应用于我的模型,例如数据库表名到类,键,外键和导航的反向属性。 EF将使用它自动神奇地创建具有继承的数据库表。 EF可以创建一个包含派生类型的所有属性的表,或者为每种类型创建单独的表,包括基类(可以是抽象的或不抽象的)。您可以搜索基类型,它将返回派生类型的正确实现。因此,您可以返回包含SalesCustomerService对象的列表。

© www.soinside.com 2019 - 2024. All rights reserved.