首先在EF代码中添加0..1关系

时间:2017-04-12 13:25:42

标签: c# entity-framework

我有以下型号:

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的关系,因此在ItemRentalId上添加了属性Item。这是可以为空的,因为不是每个Item都必须有ItemRental

是否可以仅使用注释创建此关系?

我尝试了流利的api:

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

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

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

1 个答案:

答案 0 :(得分: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&#39;拥有&#39; 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 foregn键属性基本上被忽略了。它们位于数据库中,但它们不是外键,因为它可能是预期的。这是因为一对一映射不支持将FK添加到EF模型中。这是因为一对一映射在关系数据库中存在很大问题。

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

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

如果PeopleCar表格具有相同的&#39;,则执行此规则的唯一真正方法主键(连接记录中的值相同)。要做到这一点,CarId中的Car必须是人民PK的PK和FK。这使整个架构变得混乱。当我使用它时,我宁愿在Car PersonId中命名PK / FK,并相应地对其进行配置:

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);
  }
}

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

因此,这种关系不是由数据库架构强制执行的,而是由Entity Framework本身强制执行的。这就是为什么你在使用它时必须非常小心,不要让任何人直接对数据库发脾气。