如何映射具有实体引用的值类型

本文关键字:引用 实体 类型 何映射 映射 | 更新日期: 2023-09-27 18:19:36

实体框架中的映射出现问题。

我有以下类别(简化):

public class Building
{
    public int ID { get; set; }     
    // *.. snip..* other properties
    public Location Location { get; private set; }
}
public class Location
{
    public string Street {get; set;}
    public Country country {get; set}
}
public class Country
{
    public int ID { get; set; } 
    public string Name { get; set; } 
}

Building和Country是实体,它们保存在数据库中。位置是一种值类型,应映射到与Building相同的表中。

然而,当我以这种方式映射它时,实体框架也想将Location映射到一个表,并抱怨它没有Key。我不想给它钥匙,因为它属于大楼,根本不应该是一个实体。

我见过一些变通方法,说你需要把Country放在Building类上,但这感觉不太好(而且在语义上完全错误)。

我使用的是实体框架5

如何映射具有实体引用的值类型

自从实体框架核心2发布以来,现在可以使用拥有的实体来实现这一点。

您的配置看起来像:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // ...
    modelBuilder.Entity<Building>().OwnsOne(x => x.Location);
    modelBuilder.Entity<Location>().HasOne(x => x.Country);
    // ...
}

这样,Location类的属性将成为Building类映射到的表的一部分。这意味着您将只有BuildingCountry类的表,而Building表将有一个指向Country表的外键。

我知道你已经很久没有发布这个问题了,但我认为这个答案可能对遇到这个问题的人有帮助。

在我看来,实体框架不应该允许这样的情况。

我知道您不认为Location是一个实体,但向复杂类型添加实体引用似乎也不是一个可靠的方法。一座建筑与一个国家的关系是非常直接的。一座建筑属于一个国家。因此,一个建筑模型应该包括一个国家id。你希望映射什么?

如果您希望Building表只有三列ID, Street, CountryId,并且您仍然希望保存Location模型,那么您应该使用以下复杂类型。

public class Location
{
    public string Street {get; set;}
    public int countryId {get; set}
}

但是,如果您希望您的Building表包含模型Country中的所有字段,那么这可能会导致一些棘手的情况,例如如果您想向Country模型添加新字段,或者如果您想根据新的业务案例向Country模型添加其他复杂类型或实体,会发生什么情况。

这些情况会扰乱关系概念,并在没有任何有意义的理由的情况下使您的结构过于复杂。(当然在我看来)

您可以用[NotMapped]属性标记Building类中的Location属性。

using System.ComponentModel.DataAnnotations.Schema;
public class Building
{
    [NotMapped]
    public Location Location { get; private set; }
}

希望这能解决你的问题!