Linq缓存数据值 - 主要的并发问题?

Sha*_*ica 6 c# linq concurrency linq-to-sql

这是我做的一个小实验:

MyClass obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single();
Console.WriteLine(obj.MyProperty); // output = "initial"
Console.WriteLine("Waiting..."); // put a breakpoint after this line
obj = null;
obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); // same as before, but reloaded
Console.WriteLine(obj.MyProperty); // output still = "initial"
obj.MyOtherProperty = "foo";
dataContext.SubmitChanges(); // throws concurrency exception
Run Code Online (Sandbox Code Playgroud)

当我在第3行之后点击断点时,我转到SQL查询窗口并手动将值更改为"已更新".然后我继续跑步.Linq不重新加载我的对象,但重新使用它以前在内存中的对象!这是数据并发的一个巨大问题!

你如何禁用Linq显然保留在内存中的这些隐藏的对象缓存?

编辑 - 经过反思,微软本可以在Linq框架中留下如此巨大的鸿沟,这简直是不可想象的.上面的代码是我正在做的事情的一个愚蠢的版本,我可能错过了一些细微之处.简而言之,如果您进行自己的实验来验证我的上述发现是否正确,我将不胜感激.或者,必须有某种"秘密开关"使Linq对并发数据更新具有强大的功能.但是什么?

stu*_*ith 5

这不是我以前遇到的问题(因为我不倾向于长时间保持DataContexts打开),但看起来像其他人有:

http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx


Amy*_*y B 5

LinqToSql有各种各样的工具来处理并发问题.

然而,第一步是承认有一个并发问题需要解决!

首先,DataContext的预期对象生命周期应该与UnitOfWork匹配.如果你长时间坚持一个,那么你将不得不更加努力地工作,因为这个课程不是那么用的.

其次,DataContext跟踪每个对象的两个副本.一个是原始状态,一个是已更改/可更改状态.如果你要求Id = 1的MyClass,它将返回你上次给你的同一个实例,这是改变/可更改的版本...而不是原版.它必须这样做以防止内存实例中的并发问题... LinqToSql不允许一个DataContext知道两个可更改的MyClass版本(Id = 1).

第三,DataContext不知道您的内存中更改是在数据库更改之前还是之后,因此如果没有一些指导,就无法裁断并发冲突.它看到的只是:

  • 我从数据库中读取了MyClass(Id = 1).
  • 程序员修改了MyClass(Id = 1).
  • 我将MyClass(Id = 1)发送回数据库(查看此sql以查看where子句中的乐观并发)
    • 如果数据库的版本与原始版本(乐观并发)匹配,则更新将成功.
    • 如果数据库的版本与原始版本不匹配,则更新将因并发异常而失败.

好了,现在问题已经陈述,这里有几种方法可以解决它.

你可以扔掉DataContext并重新开始.这对某些人来说有点沉重,但至少它很容易实现.

您可以通过调用DataContext.Refresh(RefreshMode, target)(在"备注"部分中引用具有许多良好并发链接的文档)来请求使用数据库值刷新原始实例或已更改/可更改的实例.这将带来客户端的更改,并允许您的代码确定最终结果应该是什么.

您可以在dbml(ColumnAttribute.UpdateCheck)中关闭并发检查.这会禁用乐观并发,并且您的代码将踩踏其他任何人的更改.也很重,也很容易实现.