LINQ to SQL/SQLMetal中如何高效地更新外键?

6

我在尝试更新外键字段时遇到了问题:

record.ForeignId = newId;

由于 SQLMetal 代码抛出 System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(),导致出现“由于对象的当前状态而无法执行操作”的错误。

我并不是第一个遇到这个问题的人:

LinqToSQL Error : Operation is not valid due to the current state of the objecthttp://social.msdn.microsoft.com/forums/en-US/linqtosql/thread/f9c4a01a-195a-4f2b-a1cb-e2fa06e28b25/ 等讨论了这个问题。

他们的解决方法如下:

record.Foreign = Database.Foreigns.Single(c => c.Id == newId);

当然,这会导致在外键上进行数据库查找,只是为了获取一个我已经知道ID的对象!那么,我如何在没有无意义查询的情况下完成此更新(如果我有许多这些FK,则可能涉及多个查询)?
3个回答

3
你可以新建一个父类实例(使用正确的 Id),将其作为现有记录状态附加到数据上下文中,然后分配子对象的 Parent 属性。
以下是一些代码示例:
int theId = 5;
Parent p = new Parent() { Id = theId};
dc.Parents.Attach(p);
child.Parent = p;

我之前不知道Attach()函数,但是尝试使用后出现了重复键错误:"无法添加已存在的实体键。" 它是通过查询数据库发现冲突的吗?还是这条记录只是在我的数据上下文中游荡着...? - Scott Stafford
DataContext 跟踪它已加载的所有记录(按主键)。如果它已加载 Id=5 的 Parent,则无法附加另一个具有 Id=5 的 Parent 实例。 - Amy B
还有没有什么办法可以查看是否缓存了某个东西,如果是,则将其取出?如果没有缓存,那么像您的答案那样创建一个新的?或者先告诉它放弃已有的缓存?或者其他任何能使这个方案起作用的方法? - Scott Stafford
1
唯一允许您从DataContext或DataTable获取实例而不触及数据库的方法是DataContext.GetChangeSet - 如果您没有针对父对象的更新/插入,这将无济于事。如果您只是将FKey设置为已知值,则打开引擎盖并发出DataContext.ExecuteCommand可能是明智的选择,而不是通过ORM进行操作。您的另一个选择是限制DataContext的生命周期,直到您可以跟踪已加载/未加载的内容。 - Amy B

1

虽然无法解决问题,但部分减轻额外负载的方法是检查现有值是否与新值不同。如果不同,将产生查询。类似于--

if(obj.PropertyID != newValue){
  obj.Property = PropertyClass.Load(newValue)
}

0

我曾经遇到过同样的问题。我创建了一个快速解决方法,可能不是最好的解决方案(它可能会影响性能),但对我来说起作用了。

在我的解决方案中,我有一个类,在类级别上声明了我的DataContext,因此我可以在整个解决方案中使用它。

在我的解决方案中,我想要做的所有事情(与您类似)就是将帐户类型ID更改为用户的不同帐户类型ID(在数据库中这些是FKs)。

由于DataContext已经加载,它不允许进行更改。解决方法?

我创建了一个函数,在该函数中创建DataContext的新实例,运行我的LINQ查询,提交更改,然后在之后处理完DataContext后进行处理。


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