映射一到零或一

那么让我们再说一遍,你有以下模型:

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

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

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

如果 PeopleCar 表具有相同主键(连接记录中的值相同),则执行此规则的唯一真正方法。要做到这一点,Car 中的 CarId 必须既是 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);
  }
}

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

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