所以,如果我理解正确,你的问题是与一个映射到零或一个关系。 您遇到的是实体框架的一个设计特性。处理一对一关系(及其可选对象)很棘手,原因很多。当你这样做时,你不能在你的模型—中指定外键,而是你的实体的主键也是主体端的外键,不能指定额外的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模式:
仔细查看:你可以看到,有没有FK在People
指到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
,People
中有CarId
。为了强制每个人只能有一辆车,PersonId
必须是唯一的Car
。但是,如果PersonId
在People
中是唯一的,那么如何添加两个或多个记录,其中PersonId
是NULL
(多辆车没有车主)?答:你不能(事实上,你可以在SQL Server 2008和更新的版本中创建一个过滤的唯一索引,但让我们暂时忘记这个技术性问题;更别说其他RDBMS了)。更不用说您指定关系的两端的情况...
如果People
和Car
表具有“相同”主键(连接记录中的值相同),则唯一可行的方法是执行此规则, 。要做到这一点,CarId
在Car
必须是一个PK和一个FK的人民PK。这使得整个模式变得混乱。当我使用这个我宁愿命名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服务器的设计限制吗?必须有办法可以定义导航属性的FK名称权限? – YesMan85
我想这是两个。在关系型数据库中有一个真正的1-1是不可能的,所以你必须在EF中做一些技巧。另外,如果向FK添加一个唯一约束,则只有一个1-1关联才是可行的。我想这就是EF进入并解决这些问题的原因,因为它引入了这个限制。 –
是的,我多玩了一会儿,我能够在SQL服务器中以黑客的方式创建关系,但无法让它在EF中工作。现在决定改变数据模型。 – YesMan85