Entity Framework 4.1 - 非键列之间的关系
Posted
技术标签:
【中文标题】Entity Framework 4.1 - 非键列之间的关系【英文标题】:Entity Framework 4.1 - Relationships between non-key columns 【发布时间】:2011-11-03 00:16:13 【问题描述】:我有 2 个相关的实体,但旧版 sql 模式本质上对同一个表有 2 个键列(不是 2 列键:见下文)。我需要创建与“假键”列的关系。有没有办法在 Entity Framework 4.1 中以声明方式执行此操作?
Public Class Client
Inherits ModelBase
<Key(), Required()>
Public Property ClientID As Decimal
<Required(), StringLength(50)>
Public Property ClientCode As String
........
Public Class ClientLocation
Inherits ModelBase
........
<Required(), StringLength(50)>
Public Property ClientCode As String
........
<ForeignKey("ClientCode")>
Public Overridable Property Client As Clients.Client
我得到的错误是:
*在模型生成过程中检测到一个或多个验证错误: System.Data.Edm.EdmAssociationConstraint: : 所有的类型 引用约束的 Dependent Role 中的属性必须是 与 Principal Role 中的相应属性类型相同。 实体“ClientLocation”上的属性“ClientCode”类型不 匹配实体 'Client' 上的属性 'ClientID' 的类型 引用约束“ClientLocation_Client”。*
因为它认为我正在尝试映射 ClientLocation.ClientCode > Client.ClientID,而当我真正尝试映射 ClientLocation.ClientCode > Client.ClientCode.. .
有什么想法吗?
谢谢!
【问题讨论】:
"...遗留的 sql 模式本质上对同一个表有 2 个键列...":您的意思是Client.ClientCode
是一个具有唯一性的列数据库中的索引?或者什么是“2个键列......但不是复合键”?你想以某种方式将ClientLocation.Client
映射到这个独特的列Client.ClientCode
?
表有2个有效key,但是第二个没有被识别为key,没有索引。例如,ClientID 可以是 4,ClientCode 可以是“FOGCREEK”。两者没有关联或依赖关系,它们只是碰巧都是独一无二的。是的,我需要使用 Client.ClientCode 映射回原始表,即使它没有在我的实体中标记为键。
啊,我明白了,那么ClientCode
就是一个普通的专栏。唯一性只是由业务逻辑意外确保的。恐怕拉迪拉夫的回答才是硬道理。
【参考方案1】:
实体框架要求在主表中的整个主键和从属表中的对应列(外键)之间建立关系。
【讨论】:
所以根据您的理解,没有办法像“映射”表和列名一样“映射”关系? 您必须像在数据库中那样映射关系。如果您的数据库没有正确设置表,EF 将无法修复它。 尽管这个答案可能是正确的,但我还不愿意在没有更多共识的情况下接受它。其他人可以验证没有类似于数据注释(这只是拉迪斯拉夫答案的附录,accept和bounty一定不要去我的答案。)
如果在 EF 中实现这样的功能,我会感到惊讶。为什么?因为您甚至无法在关系数据库中创建这样的关系。关系数据库中的外键关系(至少是 SQL Server,可能是大多数或所有其他数据库)要求主体方是主键或具有唯一键约束的列。这是有道理的,因为外键应该引用主表中的一个唯一行。
现在,EF 甚至还不支持与唯一键列的关系,只支持与主键列的关系。这是将来可能支持的东西。但是支持与非唯一和非主键列的外键关系对我来说似乎没有意义。
如果主表的目标列中的值不是唯一的,您预计会发生什么?当您尝试急切加载ClientLocation.Client
- “由于外键未引用唯一目标而无法加载导航属性'Client'”时,您是否想要一个异常或警告“确实加载了一个客户端,但还有一个,不能确保我加载了你想要的”或类似的东西?
如果您想帮自己一个忙,我会放弃这个想法,删除导航属性并考虑在您的 LINQ 查询中大量使用 Join
的方向。
【讨论】:
【参考方案3】:可能关联属性是您的答案,使用关联您可以指定要在关系的每一侧使用哪些键。
这样的一些代码:
[ForeignKey()]
[Association("SomeNameForAssociation","TheKeyInThisEntity","TheKeyOnTheAssociationTargetEntity")]
public virtual Examination Examination get; set;
如果我理解你的问题,你的代码应该改为:
Public Class Client
Inherits ModelBase
<Key(), Required()>
Public Property ClientID As Decimal
<Required(), StringLength(50)>
Public Property ClientCode As String
........
Public Class ClientLocation
Inherits ModelBase
........
<Required(), StringLength(50)>
Public Property ClientCode As String
........
<ForeignKey("ClientCode")>
<Association("ClientClientCodes","ClientCode","ClientCode")>
Public Overridable Property Client As Clients.Client
First "ClientCode" : ClientCode 中键列的名称。 第二个“ClientCode”:您要使用的 ClientCode 中键列的名称。
注意:我还没有使用过这个属性,但是它的文档和它的名字和它的参数名字表明它应该满足你的需要。
【讨论】:
我怀疑[Association]
属性在实体框架中是否有任何影响。它只是 LINQ to SQL 的映射属性。【参考方案4】:
即使不可能,您仍然可以像这样 (C#) 使用 LINQ 查询连接这两个表(另请参阅 question)。
var result = from a in ctx.Client
join b in ctx.ClientLocation
on a.ClientCode equals b.ClientCode
select new Client = a, Location = b ;
您只缺少导航属性 Client.ClientLocation 和 ClientLocation.Client。这样做有点麻烦,但还是可以的。
如果您愿意扩展 SQL 方案,您可以添加另一个表,例如 ClientLocationClient,它充当 M:N 表,外键同时指向 Client 和 ClientLocation,两者作为复合键。然后你可以像这样导航(C#)
var client = myClientLocation.ClientLocationClients.First().Client; // there's only one
但是,一旦您有一个没有相应客户端的 ClientLocation 并且当然您需要在 Client 中定义一个触发器来同步您的扩展表并将删除配置为级联并且 ClientLocation 需要是插入到客户端之前,否则触发器将失败...总的来说,我只想发出警告,这可能是一条非常危险的路线。
【讨论】:
以上是关于Entity Framework 4.1 - 非键列之间的关系的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework 4.1 InverseProperty 属性和ForeignKey
Entity Framework 4.1 Fluent API 属性
卸载 Entity Framework 4.1 六月 CTP