我可以做什么来解决在SQL Server Compact Edition数据库上的LINQ to SQL中的“行未find或更改”exception?
在使用LINQ to SQL连接(针对SQL Server Compact Edition)更新多个属性之后对DataContext执行SubmitChanges时,出现“未find或未更改的行”。 ChangeConflictException。
var ctx = new Data.MobileServerDataDataContext(Common.DatabasePath); var deviceSessionRecord = ctx.Sessions.First(sess => sess.SessionRecId == args.DeviceSessionId); deviceSessionRecord.IsActive = false; deviceSessionRecord.Disconnected = DateTime.Now; ctx.SubmitChanges();
该查询生成以下SQL:
UPDATE [Sessions] SET [Is_Active] = @p0, [Disconnected] = @p1 WHERE 0 = 1 -- @p0: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False] -- @p1: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:12:02 PM] -- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8
显而易见的问题是WHERE 0 = 1 ,在加载logging之后,我已经确认“deviceSessionRecord”中的所有属性都包含主键。 另外,当捕获“ChangeConflictException”时,没有关于为什么失败的附加信息。 我也确认了这个exception被抛出,只有一个logging在数据库中(logging我试图更新)
奇怪的是,我在代码的不同部分有一个非常相似的更新语句,它会生成以下SQL并确实更新我的SQL Server精简版数据库。
UPDATE [Sessions] SET [Is_Active] = @p4, [Disconnected] = @p5 WHERE ([Session_RecId] = @p0) AND ([App_RecId] = @p1) AND ([Is_Active] = 1) AND ([Established] = @p2) AND ([Disconnected] IS NULL) AND ([Member_Id] IS NULL) AND ([Company_Id] IS NULL) AND ([Site] IS NULL) AND (NOT ([Is_Device] = 1)) AND ([Machine_Name] = @p3) -- @p0: Input Guid (Size = 0; Prec = 0; Scale = 0) [0fbbee53-cf4c-4643-9045-e0a284ad131b] -- @p1: Input Guid (Size = 0; Prec = 0; Scale = 0) [7a174954-dd18-406e-833d-8da650207d3d] -- @p2: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:50 PM] -- @p3: Input String (Size = 0; Prec = 0; Scale = 0) [CWMOBILEDEV] -- @p4: Input Boolean (Size = 0; Prec = 0; Scale = 0) [False] -- @p5: Input DateTime (Size = 0; Prec = 0; Scale = 0) [9/4/2008 5:20:52 PM] -- Context: SqlProvider(SqlCE) Model: AttributedMetaModel Build: 3.5.21022.8
我已经确认在数据库模式和生成LINQ类的DBML中已经确定了正确的主要字段值。
我想这几乎是两个部分的问题:
- 为什么抛出exception?
- 在查看第二组生成的SQL之后,看起来好像是为了检测冲突,检查所有的字段是很好的,但我想这样做效率会很低。 这是这样的方式总是工作? 有没有一个设置来检查主键?
我一直在这个过去两个小时的战斗,所以任何帮助将不胜感激。
这很讨厌,但很简单:
检查O / R-Designer中所有字段的数据types是否与SQL表中的数据types匹配。 仔细检查可空! 在O / R-Designer和SQL中,列应该都是可空的,或者在两者中都不能为空。
例如,数据库中的NVARCHAR列“title”被标记为NULLable,并包含值NULL。 即使列在O / R映射中被标记为NOT NULLable,LINQ将成功加载它并将列String设置为null。
- 现在你改变一些东西,并调用SubmitChanges()。
- LINQ将生成一个包含“WHERE [title] IS NULL”的SQL查询,以确保标题没有被其他人改变。
- LINQ在映射中查找[title]的属性。
- LINQ会find[标题]不可空。
- 由于[标题]不可空,逻辑上它永远不可能是NULL!
- 所以,优化查询,LINQ用“where 0 = 1”代替它,SQL代表“never”。
当字段的数据types与SQL中的数据types不匹配时,或者缺less字段时,将出现相同的症状,因为LINQ将无法确保SQL数据自读取数据以来没有更改过。
DataContext上有一个名为Refresh的方法,可能在这里有所帮助。 它允许您在提交更改之前重新加载数据库logging,并提供不同的模式以确定保留哪些值。 “KeepChanges”看起来对我来说是最聪明的,它意在将我的更改与数据库中发生的任何非冲突更改合并在一起。
如果我理解正确。 🙂
我解决了这个错误,通过从服务器资源pipe理器到devise器重新编写表格并重新构build。
这也可能是由于使用多个DbContext引起的。
举个例子:
protected void loginUser(string username) { var db = new AppDbContext(); var user = db.Users.Single(u => u.Username == username); user.LastLogin = DateTime.UtcNow; db.SubmitChanges(); } protected void doSomething(object obj) { string username = "joe"; var db = new AppDbContext(); var user = db.Users.Single(u => u.Username == username); if (DateTime.UtcNow - user.LastLogin > new TimeSpan(0, 30, 0)) loginUser(username); user.Something = obj; db.SubmitChanges(); }
这段代码将不时地失败,看起来不可预知,因为用户在两个上下文中都被使用,更改并保存在一个中,然后保存在另一个中。 拥有“Something”的用户在内存中的表示与数据库中的内容不匹配,所以你得到这个潜伏的错误。
如果有一个可以跟踪这些工具的工具,那就太好了。 在生产环境中的性能显然是非常糟糕的,但是一旦你有几层逻辑都与同一个数据库进行交互,那么这些可能是非常隐秘的错误。 我想这将是一个包装,维护一个线程安全的哈希表的对象跟踪和失败早一旦看到两个上下文同时跟踪同一个对象(抛出清楚的例外,为什么和什么对象)。
首先,了解造成问题的原因是有用的。 谷歌search解决scheme应该有所帮助,你可以logging关于冲突的细节(表,列,旧值,新值),以find更好的解决scheme,以解决冲突:
public class ChangeConflictExceptionWithDetails : ChangeConflictException { public ChangeConflictExceptionWithDetails(ChangeConflictException inner, DataContext context) : base(inner.Message + " " + GetChangeConflictExceptionDetailString(context)) { } /// <summary> /// Code from following link /// https://ittecture.wordpress.com/2008/10/17/tip-of-the-day-3/ /// </summary> /// <param name="context"></param> /// <returns></returns> static string GetChangeConflictExceptionDetailString(DataContext context) { StringBuilder sb = new StringBuilder(); foreach (ObjectChangeConflict changeConflict in context.ChangeConflicts) { System.Data.Linq.Mapping.MetaTable metatable = context.Mapping.GetTable(changeConflict.Object.GetType()); sb.AppendFormat("Table name: {0}", metatable.TableName); sb.AppendLine(); foreach (MemberChangeConflict col in changeConflict.MemberConflicts) { sb.AppendFormat("Column name : {0}", col.Member.Name); sb.AppendLine(); sb.AppendFormat("Original value : {0}", col.OriginalValue.ToString()); sb.AppendLine(); sb.AppendFormat("Current value : {0}", col.CurrentValue.ToString()); sb.AppendLine(); sb.AppendFormat("Database value : {0}", col.DatabaseValue.ToString()); sb.AppendLine(); sb.AppendLine(); } } return sb.ToString(); } }
创build包装你的sumbitChanges的帮手:
public static class DataContextExtensions { public static void SubmitChangesWithDetailException(this DataContext dataContext) { try { dataContext.SubmitChanges(); } catch (ChangeConflictException ex) { throw new ChangeConflictExceptionWithDetails(ex, dataContext); } } }
然后调用提交更改代码:
Datamodel.SubmitChangesWithDetailException();
最后,在您的全局exception处理程序中loggingexception:
protected void Application_Error(object sender, EventArgs e) { Exception ex = Server.GetLastError(); //TODO }
我不知道你是否已经find了令人满意的答案,但是我发表了一个类似的问题,最终自己回答了。 事实certificate,数据库的NOCOUNT默认连接选项已打开,导致用Linq to Sql进行的每个更新都导致ChangeConflictException。 你可以在这里参考我的文章。
我通过添加(UpdateCheck = UpdateCheck.Never)
到所有[Column]
定义来解决这个问题。
不过,感觉不到一个合适的解决scheme。 在我的情况下,它似乎与这个表有一个关联,从另一个表中删除行的事实。
这是在Windows Phone 7.5上。
我知道这个问题早已得到解答,但是在这里我花了几个小时的时间把我的头撞在墙上,我只是想分享一下我的解决scheme,这个解决scheme并不涉及这个线程中的任何项目:
caching!
我的数据对象的select()部分正在使用caching。 当更新对象的时候,Row Not Found或者Changed错误出现了。
几个答案确实提到了使用不同的DataContext的,回想起来可能是发生了什么事情,但它并没有立即让我思考caching,所以希望这会帮助别人!
我最近遇到了这个错误,发现问题不在我的数据上下文中,而是在上下文调用Commit之后触发内部更新语句。 触发器试图用一个空值更新一个不可为空的字段,并且导致上下文与上面提到的消息出错。
我添加这个答案只是为了帮助其他人处理这个错误,而不是在上面的答案中find解决scheme。
我也有这个错误,因为使用了两个不同的上下文。 我通过使用单个数据上下文解决了这个问题。
在我的情况下,问题是与服务器范围的用户选项。 以下:
https://msdn.microsoft.com/en-us/library/ms190763.aspx
我启用了NOCOUNT选项,希望获得一些性能优势:
EXEC sys.sp_configure 'user options', 512; RECONFIGURE;
事实certificate这是为了打破Linq对受感染行的检查(尽可能从.NET资源中找出),导致ChangeConflictException
重置选项以排除512位解决了问题。
这是你需要重写C#代码上的这个错误:
try { _db.SubmitChanges(ConflictMode.ContinueOnConflict); } catch (ChangeConflictException e) { foreach (ObjectChangeConflict occ in _db.ChangeConflicts) { occ.Resolve(RefreshMode.KeepChanges); } }
在使用qub1n的答案后,我发现我的问题是我无意中声明了一个数据库列是十进制(18,0)。 我正在分配一个十进制值,但数据库正在改变它,剥离小数部分。 这导致行更改的问题。
只要添加这个,如果有其他人遇到类似的问题。