使用实体框架更新主键值

Pau*_*mke 50 .net vb.net sql-server entity-framework

我正在尝试从实体框架中更新复合主键的一个值,并且我收到此错误:"属性'CustomerID'是对象的关键信息的一部分,无法修改."

这是我的代码:

Dim customer As Customer = (From c In db.Customer Where c.CustomerID = "xxx" AndAlso c.SiteKey = siteKey).FirstOrDefault
customer.CustomerID = "fasdfasdf"
db.SaveChanges()
Run Code Online (Sandbox Code Playgroud)

这似乎太简单了.是否真的无法在实体框架内更新主键?我找不到有关该主题的任何文档.谢谢!

小智 59

我有博士学位 在cs中 - 在数据库领域,所以这个答案将与程序员的观点略有不同.对于所有关于奥利弗Hanappi,一键便可偶尔不改变,如果它不是一个代理键.例如自然键或复合键.例如.可以在美国更改您的SSN.但是,许多程序员多年来都会认为这是一个不可更改的密钥,并将其用作原样.更改由外键组成的复合主键更为常见.

我正在处理一个有三元关系的数据库.特别是三个实体(外键是各自表中的代理主键).但是,要在更改第三个实体时保留两个实体之间的关系,需要更改交集表的一部分(在MSDN上也称为纯连接表)主键.这是一个有效的设计,只能通过删除三元关系交集表并将其替换为两个二进制关系表(可能有自己的代理键)来改进.EF会处理这个问题.此设计更改将使(多个 - >多个) - >多个或Parent1-Parent2 - >子孙模型(如果不清楚,请阅读下面的示例).实体框架可以正常工作,因为每个关系实际上是一对多的关系.但从DB的角度来看,这是一个疯狂的设计.让我举个例子说明原因.

考虑课程,课堂和讲师在课堂上彼此相关联.类可以包括:CourseID,ClassroomID,InstructorID作为外键,并包含一个包含所有三个的复合主键.虽然是一个清晰,简洁的三元模型(三向关系),但我们可以将其分解为二元关系.这将给出两个交集表.添加代理键将满足EF,如下所示:

类(SurrogateKeyClass,InstructorID,CourseID)

ClassRoomUsed(SurrogateKeyClassroomUsed,SurrogateKeyClass,ClassRoomID)

这种设计的问题在于,我们可以将相同的课程和教师多次关联,这是之前的模型所避免的.为了避免这个问题,你可以在数据库中添加一个约束,以获得两个ID字段的唯一性,但是当你只处理代理键时,你为什么要这样做呢?但是,这个解决方案可以尽我所能地工作.然而,这不是逻辑数据库设计,因为DB中需要非自然的唯一约束.

但是,如果您不想更改数据库或无法更改数据库,则这是第二种解决方案:交叉/关联表就是这样,将两个实体或更多实体链接在一起的链接.如果更改,请删除关联并重新创建具有相应外键(导航属性)的新关联.这意味着您不会被允许在任何关系中要求子实体,但这是非常常见的.

我建议实体框架(将来)允许我们这些能够设计优雅数据库模型的人在我们想要的时候更改交集/关联表中的部分键!

免费的另一个例子:

考虑学生,课程,年级协会.学生通过成绩与课程相关联.通常这是Student和一个Course之间的多对多关联,在关联表中有一个名为grade的附加字段(关联表有有效载荷数据,如等级,交集表没有有效载荷,在MSDN中称为纯连接表,在一个地方租赁):

学生(StudentID,....)

课程(CourseID,...)

考试(StudentID,CourseID,年级)

如果有人从下拉列表中输入数据并将学生放入错误的班级,您希望他们稍后通过再次选择下拉列表并选择其他课程来更改它.在后台,您需要从Taking表中删除EF对象并重新创建它,而不会丢失等级(如果有).简单地更改外键课程ID似乎是一个更好的选择.如果这个似乎是做作的话,想出你自己的协会,但作为教授,这对我来说很自然.

结论:当你有一串关系时,最好不要允许级联和/或改变FK,但是存在需要它的合理/逻辑场景,即使不推荐作为一般的最佳实践.

此问题可能会出现以下异常,具体取决于您是分别更改模型中的导航属性还是键属性:

发生了引用完整性约束冲突:当依赖对象为Unchanged时,除非将其设置为关联的主体对象,否则无法更改作为参照完整性约束一部分的主键属性.必须跟踪主要对象,并且不标记为删除.

属性"X"是对象的关键信息的一部分,无法修改.


Shi*_*iji 21

您无法通过实体框架更新主键,因为实体框架不知道要更新哪个数据库行.

但是,如果您确实需要这样做,则可以编写更新主键的存储过程,然后从实体框架执行存储过程.

  • @Nathan,我同意你的意见,它不应该是必须的,我们只使用自动增量id.然而,问题是它是否可能,这是一种方法. (9认同)
  • -1您永远不需要这样做。如果这样做,数据库设计是错误的。 (2认同)
  • 交叉表确实提供了有效的用例。我的情况是系统中的“案例”可以通过各种方式链接在一起。EG:一个可以设置为另一个的副本。具有所有FK的交叉表记录了CaseFrom,CaseTo和LinkType的唯一组合。有时指定的重复项会更改。我希望能够更新行(更容易审核),而不是删除/添加。 (2认同)
  • 我有一个表来控制物品的位置;每个项目都有 4 个字段来标识其*唯一*位置。所以,从逻辑上讲,这4个字段组成了一个PK。然而,改变位置是可取的,因此 PK 需要改变。当然,我可以添加一个带有唯一键的表来指向位置,但这很愚蠢。 (2认同)

Nat*_*n W 11

你不能并且有充分的理由.查看KM评论.

我说你可以做的一件事是有两个表,一个是匿名数据,一个是在登录后存储真实用户数据.

或者你的(我没有经过测试或曾经做过)可以使用这种表布局:

---Customers----
AutoNumber PK <- This links to all other tables in your database, and does NOT change.
CustomerID  <- This can change.
CustomerType <- Anonymous or logged in.  
Run Code Online (Sandbox Code Playgroud)

当他们登录时,您将CustomerType和CustomerID更改为您需要的内容.

所以你的查询看起来像这样:

Dim customer As Customer = (From c In db.Customer _
                            Where c.CustomerID = {Some temp ID} _
                            AndAlso c. CustomerType = "Anonymous").FirstOrDefault
// After user logs in.
customer.CustomerID = {Make a new user ID here}
customer.CustomerType = "LoggedIn" {or what ever}
db.SaveChanges()
Run Code Online (Sandbox Code Playgroud)

请注意,自动编号主键永远不会更改.这样,您与Customers表的关系中的任何表仍然有效,并且不必对主键进行级联更新(这就像用铅笔刺伤自己的眼睛一样).

  • 我不同意这一点:"有充分的理由." 我知道这在某些时候是不好的做法,但也有例外.例如,如果我使用自然/多部分密钥来存储电话号码,为什么我只能更新电话类型而被迫创建代理键?EF应该远离我的业务并做我告诉它的事情.这是系统的一个严重限制. (42认同)
  • 我会给你一个他们可以创建的问题的真实例子,我工作的人决定使用{Road Name}({Suburb})_ {Segment Number}为我们的道路网络创建ID是个好主意.例如ACACIA ST(KLN)_50现在这是主要的关键并且与许多事情相关联,例如去年的财务记录,一些估价等等和一堆其他表格,它也是一个"智能钥匙",因为它包含业务数据.现在猜猜ACACIA ST出了什么问题,说它是ACACI ST还是什么,现在你的ID与真实的道路名称不同步,人们认为他们应该改变它... (4认同)
  • "有些时候不好练习"这种做法一直很糟糕.我从未见过能够更改主键的情况.如果你只有一个表,那么没什么大不了的,但是如果你有链接表并且你开始搞乱更改主键,它可能会变得非常混乱. (3认同)
  • 这需要通过财务记录全部更新,因为现在ID链接已损坏。如果他们只使用了一个普通数字并且道路发生了变化,那么他们只需更改道路名称并保留ID不变,一切都将保持不变。这是我永远不使用由其他数据位组成的智能键的第一大理由,这给您带来了很多不值得的问题。 (2认同)
  • @Fëanor谁说使用ID的其他数据存储在数据库中?并非所有内容都在一个系统中,因此级联更新无济于事。 (2认同)
  • 提出的问题是“使用实体框架更新主键值”。这个问题简单而直接,但您提出了一种无法回答问题的方法(尽管这是推荐的方法)。如果您正在使用一个绝对不能对架构进行任何更改但仍需要更新 PK 字段的数据库,该怎么办? (2认同)