映射一到零或一
那么让我们再说一遍,你有以下模型:
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])
所以这意味着我们在模型中的 CarId
和 PersonId
foregn 键属性基本上被忽略了。它们位于数据库中,但它们不是外键,因为它可能是预期的。这是因为一对一映射不支持将 FK 添加到 EF 模型中。这是因为在关系数据库中,一对一映射存在很大问题。
这个想法是每个人都可以拥有一辆汽车,而这辆汽车只能属于那个人。或者可能存在人员记录,其中没有与他们相关联的汽车。
那怎么能用外键表示呢?显然,在 Car
中可能会有一个 PersonId
,而在 tihuan 中可能会有一个 CarId
。为了强制每个人只能拥有一辆车,PersonId
必须在 Car
中独一无二。但是如果 PersonId
在 People
中是独一无二的,那么如何添加两个或更多记录,其中 PersonId
是 NULL
(多个车辆没有车主)?答:你不能(实际上,你可以在 SQL Server 2008 及更新版本中创建一个过滤的唯一索引,但让我们暂时忘掉这个技术性;更不用说其他 RDBMS 了)。更不用说你指定关系的两端的情况……
如果 People
和 Car
表具有相同主键(连接记录中的值相同),则执行此规则的唯一真正方法。要做到这一点,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);
}
}
不理想,但也许更好一点。但是,在使用此解决方案时,你必须保持警惕,因为它违反了通常的命名约定,这可能会让你误入歧途。这是从这个模型生成的模式:
因此,这种关系不是由数据库模式强制实施的,而是由实体框架本身强制实施的。这就是为什么你在使用它时必须非常小心,不要让任何人直接使用数据库。