Entity Framework 4.1 - 非主键列之间的关系

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:引用约束的从属角色中所有属性的类型必须与主要角色中对应属性的类型相同。实体“ClientLocation”上属性“ClientCode”的类型与实体“Client”上属性“ClientID”的类型不匹配,在引用约束“ClientLocation_Client”中。

因为它认为我正在尝试将ClientLocation.ClientCode > Client.ClientID映射,而我实际上是要将ClientLocation.ClientCode > Client.ClientCode进行映射...

有什么想法吗?

谢谢!


“...传统的SQL模式基本上为同一张表格有两个关键列...”:您是指Client.ClientCode是数据库中具有唯一索引的列吗?或者这两个关键列是什么,但不是复合关键字?您想以某种方式将ClientLocation.Client映射到此唯一列Client.ClientCode吗? - Slauma
该表有两个有效键,但第二个键未被标识为键,并且没有索引。例如,ClientID可以是4,而ClientCode可以是“FOGCREEK”。这两个键没有关联或依赖关系,它们只是同时唯一。是的,我需要使用Client.ClientCode映射回原始表,即使它在我的实体中没有标记为键。 - Tom Halladay
啊,我明白了,那么ClientCode只是一个普通的列。唯一性只是因为业务逻辑的偶然保证。恐怕Ladilav的答案就是最终答案。 - Slauma
4个回答

3

Entity Framework 要求在主表中的整个主键和从属表中相应列(外键)之间建立关系。


那么根据您的理解,我们无法像“映射”表和列名一样“映射”关系,对吗? - Tom Halladay
你必须按照在数据库中执行的方式映射关系。如果你的数据库没有正确设置表,EF 也无法修复它。 - Ladislav Mrnka
1
尽管这个答案可能是正确的,但在没有更多共识的情况下,我还不愿意立即接受它。是否有其他人能够验证,是否有类似于数据注释 <Table("oldTableName")> 和 <Column("oldColumnName")> 的方法来帮助覆盖关系字段? - Tom Halladay

2
即使不可能,您仍然可以使用LINQ查询将这两个表连接起来(C#)。请参见此问题
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,它充当具有对 Client 和 ClientLocation 的外键和两个组合键的 M:N 表。然后,您可以像以下方式进行导航(C#):
var client = myClientLocation.ClientLocationClients.First().Client; // there's only one

这一行代码会在你有一个没有对应客户的客户位置时失败。当然,你需要在客户中定义一个触发器来同步你的扩展表,并配置删除为级联方式。而且,在插入客户之前必须先插入客户位置,否则触发器将失败...总的来说,我只是想提醒一下,这可能是一条相当危险的路线。

0

可能关联属性是您的答案,使用关联可以指定在关系的每一侧中使用哪些键。

有些代码如下:

[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  

第一个 "ClientCode":ClientCode 中关键列的名称。 第二个 "ClientCode":您想要使用的 ClientCode 中的关键列的名称。

注意:我尚未使用此属性,但其文档、名称和参数名称表明它应该能够满足您的需求。


我怀疑[Association]属性在Entity Framework中没有任何作用,它只是LINQ to SQL的映射属性。 - Slauma

0

(这只是Ladislav答案的附录,接受和悬赏不能给我的答案。)

如果EF将来实现这样的功能,我会感到惊讶。为什么?因为在关系型数据库中,你甚至无法创建这样的关系。在关系型数据库中,外键关系(至少在SQL Server和可能大多数或所有其他关系型数据库中)要求主表的一侧是一个列,该列既是主键,也具有唯一键约束。这是有道理的,因为外键应该引用主表中的一个唯一行

现在,EF甚至还不支持与唯一键列的关系,只支持与主键列的关系。这是未来可能支持的内容。但是,支持对非唯一和非主键列的外键关系甚至对我来说都没有意义。

如果主表的目标列中的值不是唯一的,您希望发生什么?例如,当您尝试急切加载ClientLocation.Client时,您希望出现异常-“无法加载导航属性'Client',因为外键没有引用唯一目标”,还是警告“已经加载了一个客户端,但还有另一个,无法确保我加载了您想要的那个”或类似的内容?

如果你想对自己有所帮助,我建议放弃这个想法,移除导航属性,并考虑在 LINQ 查询中大量使用 Join


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接