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对并发数据更新具有强大的功能.但是什么?
这不是我以前遇到的问题(因为我不倾向于长时间保持DataContexts打开),但看起来像其他人有:
http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx
LinqToSql有各种各样的工具来处理并发问题.
然而,第一步是承认有一个并发问题需要解决!
首先,DataContext的预期对象生命周期应该与UnitOfWork匹配.如果你长时间坚持一个,那么你将不得不更加努力地工作,因为这个课程不是那么用的.
其次,DataContext跟踪每个对象的两个副本.一个是原始状态,一个是已更改/可更改状态.如果你要求Id = 1的MyClass,它将返回你上次给你的同一个实例,这是改变/可更改的版本...而不是原版.它必须这样做以防止内存实例中的并发问题... LinqToSql不允许一个DataContext知道两个可更改的MyClass版本(Id = 1).
第三,DataContext不知道您的内存中更改是在数据库更改之前还是之后,因此如果没有一些指导,就无法裁断并发冲突.它看到的只是:
好了,现在问题已经陈述,这里有几种方法可以解决它.
你可以扔掉DataContext并重新开始.这对某些人来说有点沉重,但至少它很容易实现.
您可以通过调用DataContext.Refresh(RefreshMode, target)(在"备注"部分中引用具有许多良好并发链接的文档)来请求使用数据库值刷新原始实例或已更改/可更改的实例.这将带来客户端的更改,并允许您的代码确定最终结果应该是什么.
您可以在dbml(ColumnAttribute.UpdateCheck)中关闭并发检查.这会禁用乐观并发,并且您的代码将踩踏其他任何人的更改.也很重,也很容易实现.