如何为具有多种父类型的子场景编写 EF 代码优先映射

Posted

技术标签:

【中文标题】如何为具有多种父类型的子场景编写 EF 代码优先映射【英文标题】:How to write EF Code first mapping for Child with Many Parent Types scenario 【发布时间】:2011-12-11 10:09:29 【问题描述】:

我有 4 个课程。供应商、客户、员工和地址。前 3 种类型中的任何一种都可以有“n”个地址。所以类看起来像这样;

class Address 

    int Id  get; set; 
    int ParentId  get; set;   // NOTE: This is the FK.
    IAggregateRoot Parent  get; set;  // EXAMPLE: Supplier, Customer, Employee ..

    // rest of the address fields.


class Supplier : IAggregateRoot

    int Id  get; set; 
    virtual List<Address> Addresses  get; set; 

    // rest of the supplier details.

    AddAddress(Address address)
    
        address.Parent = this;
        address.ParentId = this.Id;

        Addresses.Add(address);
    


class Customer : IAggregateRoot

    int Id  get; set; 
    virtual List<Address> Addresses  get; set; 

    // rest of the customer details.

    AddAddress(Address address)
    
        address.Parent = this;
        address.ParentId = this.Id;

        Addresses.Add(address);
    


class Employee : IAggregateRoot

    int Id  get; set; 
    virtual List<Address> Addresses  get; set; 

    // rest of the employee details.

    AddAddress(Address address)
    
        address.Parent = this;
        address.ParentId = this.Id;

        Addresses.Add(address);
    

如何编写地址的父属性的映射?或者更好的方法/设计来完成这种场景?

【问题讨论】:

您需要父母提供地址吗?你多久搜索一次那个方向?我会在数据库中将其表示为具有地址 ID 的父母。然后对不同类型的父母进行三个单独的(可能是手动的)查询。否则,您必须使用可能具有地址的那些类型的继承层次结构。它们在问题域中是否以任何方式在逻辑上相关,或者这只是一个代码问题? @MerlynMorgan-Graham 我不希望每个 Parent 类型都有很多 Address 类。因此,我已将我的类图标准化为具有许多父类型的 Address 类。如果我在 Old-School SQL 存储过程和 IDataReader 方法中执行此操作,我可以管理它,但我无法弄清楚我必须如何为此编写流利的配置。不,我不想从地址查询/导航到父级。 不,您的地址类没有很多父类型。它有 1 个父类型—— IAggregateRoot。 EF 会认为这是一个 1..N 关系,其中每个地址只有 1 个父级。 【参考方案1】:

您希望 EF 如何在数据库中解决这样的映射? Address.ParentId 列是否会引用 Customer、Supplier 或 Employee 的主键?

如果您摆脱从地址到 IAggregateRoot 的导航,您也许可以做到这一点。也许试试这个:

protected override void OnModelCreating(DbModelBuilder modelBuilder)

    modelBuilder.Entity<Employee>.HasMany(p => p.Addresses).WithOptional()
        .Map(d => d.MapKey("EmployeeId"));
    modelBuilder.Entity<Supplier>.HasMany(p => p.Addresses).WithOptional()
        .Map(d => d.MapKey("SupplierId"));
    modelBuilder.Entity<Customer>.HasMany(p => p.Addresses).WithOptional()
        .Map(d => d.MapKey("CustomerId"));

【讨论】:

【参考方案2】:

如果 Employee、Customer 和 Supplier 不共享父实体(类)并且不遵循继承映射,则您不能这样做。您的地址必须与@olivehour 提到的每个相关实体有单独的关系。

【讨论】:

【参考方案3】:

来自 OP 上的 cmets:

我不希望每个 Parent 类型都有很多 Address 类。不,我不想从地址查询/导航到父级。

在您的域逻辑或用户工作流程中是否有某些东西可以使多个父母共享同一个地址成为真实场景?例如,地址共享这一事实是否会以重要的方式出现在 UI 中,除非他们同时登录并面对面交换信息?

如果不是,我不确定两个相同的地址是否共享相同的 ID 是否真的很重要,并且我不确定潜在的数据重复是否重要。

在这种情况下,我推荐 KISS。

制作三个独立的映射表,不要为任何继承而烦恼,然后就完成了:)

SupplierToAddressMap:
SupplierId  AddressId

CustomerToAddressMap:
CustomerId  AddressId

EmployeeToAddressMap:
EmployeeId  AddressId

【讨论】:

以上是关于如何为具有多种父类型的子场景编写 EF 代码优先映射的主要内容,如果未能解决你的问题,请参考以下文章

如何为不同的数据源实现数据映射器/存储库持久性模式?

EF 4.1 复杂关系的实体映射

如何为具有泛型类型的箭头函数编写流类型

使用 Ramda 以声明性方式编写具有优先级的 Boolean -> 字符串映射器

如何为具有 redux 连接组件作为孙子的组件编写故事书故事?

代码优先迁移:如何为新属性设置默认值?