Kev*_*vin 91 .net linq linq-to-sql
在使用LINQ to SQL连接(针对SQL Server Compact Edition)更新一些属性后,在向DataContext执行SubmitChanges时,我得到"未找到或更改行".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();
Run Code Online (Sandbox Code Playgroud)
该查询生成以下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
Run Code Online (Sandbox Code Playgroud)
显而易见的问题是WHERE 0 = 1,加载记录后,我已经确认"deviceSessionRecord"中的所有属性都是正确的,以包含主键.此外,当捕获"ChangeConflictException"时,没有关于其失败原因的其他信息.我还确认这个异常会被数据库中的一条记录抛出(我正在尝试更新的记录)
奇怪的是,我在代码的不同部分有一个非常相似的更新语句,它生成以下SQL,确实更新了我的SQL Server Compact Edition数据库.
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
Run Code Online (Sandbox Code Playgroud)
我已经确认已在Database Schema和生成LINQ类的DBML中识别出正确的主字段值.
我想这几乎是一个两部分的问题:
在过去的两个小时里,我一直在与之抗争,所以任何帮助都会受到赞赏.
Sam*_*Sam 182
多数令人讨厌,但很简单:
检查O/R-Designer中所有字段的数据类型是否与SQL表中的数据类型匹配. 双重检查可空!列应该在O/R-Designer和SQL中都可以为空,或者在两者中都不可为空.
例如,NVARCHAR列"title"在数据库中标记为NULLable,并包含值NULL.即使在O/R-Mapping中将列标记为NOT NULLable,LINQ也会成功加载它并将column-String设置为null.
当字段的数据类型与SQL中的数据类型不匹配或缺少字段时,将出现相同的症状,因为LINQ将无法确保自读取数据后SQL数据未发生更改.
Tom*_*bes 20
首先,了解导致问题的原因很有用.谷歌搜索解决方案应该有所帮助,您可以记录有关冲突的详细信息(表,列,旧值,新值),以便找到解决冲突的更好解决方案:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
创建包装sumbitChanges的帮助器:
public static class DataContextExtensions
{
public static void SubmitChangesWithDetailException(this DataContext dataContext)
{
try
{
dataContext.SubmitChanges();
}
catch (ChangeConflictException ex)
{
throw new ChangeConflictExceptionWithDetails(ex, dataContext);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后调用提交更改代码:
Datamodel.SubmitChangesWithDetailException();
Run Code Online (Sandbox Code Playgroud)
最后,在全局异常处理程序中记录异常:
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
//TODO
}
Run Code Online (Sandbox Code Playgroud)
Chr*_*ini 10
这也可能是由于使用多个DbContext引起的.
例如:
protected async Task loginUser(string username)
{
using(var db = new Db())
{
var user = await db.Users
.SingleAsync(u => u.Username == username);
user.LastLogin = DateTime.UtcNow;
await db.SaveChangesAsync();
}
}
protected async Task doSomething(object obj)
{
string username = "joe";
using(var db = new Db())
{
var user = await db.Users
.SingleAsync(u => u.Username == username);
if (DateTime.UtcNow - user.LastLogin >
new TimeSpan(0, 30, 0)
)
loginUser(username);
user.Something = obj;
await db.SaveChangesAsync();
}
}
Run Code Online (Sandbox Code Playgroud)
此代码会不时地以一种看似不可预测的方式失败,因为用户在两个上下文中使用,更改并保存在一个中,然后保存在另一个中.拥有"Something"的用户的内存中表示与数据库中的内容不匹配,因此您会遇到这个潜伏的错误.
防止这种情况的一种方法是编写任何可能被称为库方法的代码,使其采用可选的DbContext:
protected async Task loginUser(string username, Db _db = null)
{
await EFHelper.Using(_db, async db =>
{
var user = await db.Users...
... // Rest of loginUser code goes here
});
}
public class EFHelper
{
public static async Task Using<T>(T db, Func<T, Task> action)
where T : DbContext, new()
{
if (db == null)
{
using (db = new T())
{
await action(db);
}
}
else
{
await action(db);
}
}
}
Run Code Online (Sandbox Code Playgroud)
所以现在你的方法需要一个可选的数据库,如果没有,那就自己去做一个.如果有它只是重用传入的内容.帮助方法可以很容易地在您的应用程序中重用此模式.