【问题标题】:Adding 0..1 relation in EF code first首先在 EF 代码中添加 0..1 关系
【发布时间】:2017-04-12 13:25:42
【问题描述】:

我有以下型号:

public class ItemRental
{
    [Key]
    public Int32 ItemRentalId { get; set; }

    public Int32 ItemId { get; set; }
    public Int32 ChargeItemId { get; set; }

    [ForeignKey("ItemId")]
    public Item Item { get; set; }

    [ForeignKey("ChargeItemId")]
    public Item ChargeItem { get; set; }
}

public class Item
{
    [Key]
    public Int32 ItemId { get; set; }

    public Int32? ItemRentalId { get; set; }

    [ForeignKey("ItemRentalId")]
    public ItemRental ItemRental { get; set; }
}

ItemRentalItem 具有 1..N 关系,并且与 ChargeItem 具有 1..N 关系。

问题是我需要从Item 回到ChargeItem 的关系,所以在Item 上添加了属性ItemRentalId。这是可以为空的,因为不是每个Item 都必须有一个ItemRental

是否可以仅使用注释来创建这种关系?

我尝试了流畅的api:

       modelBuilder.Entity<Item>()
                    .HasOptional(m => m.ItemRental)
                    .WithRequired(c => c.ChargeItem)
                    .Map(p => p.MapKey("ItemRentalId"));

但是在进行迁移之后,它是否没有使用 ChargeItemId 作为关系。

问题是当我运行此迁移时,它不会将 ItemRentalId 作为 FK 导航属性。

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    因此,如果我理解正确,您的问题在于将一映射到零或一关系。 您正在体验的是实体框架的设计特性。由于很多原因,处理一对一的关系(以及它们的可选对应关系)是很棘手的。当你这样做时,你不能在你的模型中指定外键——相反,你的实体的主键也将是主体端的外键,不能指定额外的 FK。

    有关此的更多详细信息,请参见下文。


    映射一对零或一

    假设您有以下模型:

    public class Person
    {
      public int PersonId { get; set; }
      public string Name { get; set; }
    }
    
    public class Car
    {
      public int CarId { get; set; }
      public string LicensePlate { get; set; }
    }
    
    public class MyDemoContext : DbContext
    {
      public DbSet<Person> People { get; set; }
      public DbSet<Car> Cars { get; set; }
    }
    

    现在你想设置它,以便你可以表达以下规范:一个人可以有一辆或零辆汽车,每辆汽车完全属于一个人(关系是双向的,所以如果 CarA 属于 PersonA,那么PersonA“拥有”CarA)。

    所以让我们稍微修改一下模型:添加导航属性和外键属性:

    public class Person
    {
      public int PersonId { get; set; }
      public string Name { get; set; }
      public int CarId { get; set; }
      public virtual Car Car { get; set; }
    }
    
    public class Car
    {
      public int CarId { get; set; }
      public string LicensePlate { get; set; }
      public int PersonId { get; set; }
      public virtual Person Person { get; set; }
    }
    

    以及配置:

    public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
    {
      public CarEntityTypeConfiguration()
      {
         this.HasRequired(c => c.Person).WithOptional(p => p.Car);                        
      }
    }    
    

    此时这应该是不言自明的。这辆车有一个必需的人 (HasRequired()),而这个人有一辆可选的汽车 (WithOptional())。同样,从哪一方配置此关系并不重要,只是在使用 Has/With 和 Required/Optional 的正确组合时要小心。从Person 方面来看,它看起来像这样:

    public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
    {
      public PersonEntityTypeConfiguration()
      {
         this.HasOptional(p => p.Car).WithOptional(c => c.Person);                        
      }
    }    
    

    现在让我们看看 db 架构:

    仔细看:可以看到People中没有FK可以引用Car。此外,Car 中的 FK 不是PersonId,而是CarId。这是 FK 的实际脚本:

    ALTER TABLE [dbo].[Cars]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Cars_dbo.People_CarId] FOREIGN KEY([CarId])
    REFERENCES [dbo].[People] ([PersonId])
    

    所以这意味着我们在模型中拥有的CarIdPersonId 外键属性基本上都被忽略了。它们在数据库中,但它们不是外键,正如预期的那样。这是因为一对一映射不支持将 FK 添加到您的 EF 模型中。那是因为一对一的映射在关系数据库中是很成问题的。

    这个想法是每个人都可以拥有一辆车,而那辆车只能属于那个人。或者可能有人员记录,其中没有与之关联的汽车。

    那么如何用外键表示呢?显然,Car 中可能有一个PersonIdPeople 中可能有一个CarId。为了强制每个人只能拥有一辆汽车,PersonIdCar 中必须是唯一的。但是如果PersonIdPeople 中是唯一的,那么如何在PersonIdNULL 的地方添加两条或更多记录(不止一辆没有车主的汽车)?答:不能(实际上,您可以在 SQL Server 2008 和更新版本中创建过滤的唯一索引,但让我们暂时忘记这个技术性;更不用说其他 RDBMS)。更不用说指定关系两端的情况了......

    如果PeopleCar 表具有“相同”主键(连接记录中的值相同),则唯一真正执行此规则的方法。要做到这一点,Car 中的 CarId 必须既是 People PK 的 PK 又是 FK。这使整个架构变得一团糟。当我使用它时,我宁愿将 PK/FK 命名为 Car PersonId,并进行相应的配置:

    public class Person
    {
      public int PersonId { get; set; }
      public string Name { get; set; }        
      public virtual Car Car { get; set; }
    }
    
    public class Car
    {        
      public string LicensePlate { get; set; }
      public int PersonId { get; set; }
      public virtual Person Person { get; set; }
    }
    
    public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
    {
      public CarEntityTypeConfiguration()
      {
         this.HasRequired(c => c.Person).WithOptional(p => p.Car);
         this.HasKey(c => c.PersonId);
      }
    }
    

    不理想,但可能会好一点。不过,在使用此解决方案时您必须保持警惕,因为它违反了通常的命名约定,这可能会导致您误入歧途。这是从此模型生成的架构:

    所以这种关系不是由数据库模式强制执行的,而是由实体框架本身强制执行的。这就是为什么你在使用它时必须非常小心,不要让任何人直接对数据库进行修改。

    【讨论】:

    • 那么这是 EF 或 Sql server 的设计限制吗?必须有方法可以定义导航属性的 FK 名称吗?
    • 我猜两者都是。在关系数据库中不可能有真正的 1-1,因此您必须在 EF 中做一些技巧。此外,仅当您向 FK 添加唯一约束时,1-1 关联才可行。我想这就是 EF 的用武之地,它解决了这些问题,因为它引入了这个限制。
    • 是的,我用它玩了一点,我能够在 SQL 服务器中以一种 hacky 方式创建关系,但无法让它在 EF 中工作。现在决定更改数据模型。
    猜你喜欢
    • 1970-01-01
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    • 2012-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-27
    相关资源
    最近更新 更多