LINQ 中的数据冲突
Posted
技术标签:
【中文标题】LINQ 中的数据冲突【英文标题】:Data Conflict in LINQ 【发布时间】:2010-09-07 03:41:58 【问题描述】:使用SubmitChanges()
进行更改时,LINQ 有时会因ChangeConflictException
异常而终止,并显示错误消息Row not found or changed
,而没有任何指示存在冲突的行或具有冲突更改的字段,当另一个用户更改了该行中的一些数据。
有没有办法确定哪一行有冲突以及它们发生在哪些字段中,还有没有办法让 LINQ 忽略该问题并简单地提交数据?
此外,是否有人知道此异常是否发生在行中的任何数据已更改时,还是仅当 LINQ 试图更改的字段中的数据已更改时发生?
【问题讨论】:
【参考方案1】:这是一种查看冲突位置的方法(这是一个 MSDN 示例,因此您需要大量自定义):
try
db.SubmitChanges(ConflictMode.ContinueOnConflict);
catch (ChangeConflictException e)
Console.WriteLine("Optimistic concurrency error.");
Console.WriteLine(e.Message);
Console.ReadLine();
foreach (ObjectChangeConflict occ in db.ChangeConflicts)
MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
Customer entityInConflict = (Customer)occ.Object;
Console.WriteLine("Table name: 0", metatable.TableName);
Console.Write("Customer ID: ");
Console.WriteLine(entityInConflict.CustomerID);
foreach (MemberChangeConflict mcc in occ.MemberConflicts)
object currVal = mcc.CurrentValue;
object origVal = mcc.OriginalValue;
object databaseVal = mcc.DatabaseValue;
MemberInfo mi = mcc.Member;
Console.WriteLine("Member: 0", mi.Name);
Console.WriteLine("current value: 0", currVal);
Console.WriteLine("original value: 0", origVal);
Console.WriteLine("database value: 0", databaseVal);
让它忽略问题并提交:
db.SubmitChanges(ConflictMode.ContinueOnConflict);
【讨论】:
我将不得不将此添加到我的代码中。我刚刚推出了一个遇到“未找到行”异常的应用程序。 小心,ConflictMode.ContinueOnConflict 无论如何都不会让它提交,它所做的是让它尝试做所有的更新,所以你会立即得到所有的冲突,而不是在第一个冲突上停下来,但它确实会回滚。 要让它忽略这个问题,你需要在你的 catch 上调用 ResolveAll(KeepChanges) 天哪,我以前怎么不知道 ChangeConflicts 属性?哦,我本可以节省的时间和理智!啊!【参考方案2】:这些(您可以在部分类中添加到数据上下文中,这可能有助于您了解其工作原理:
public void SubmitKeepChanges()
try
this.SubmitChanges(ConflictMode.ContinueOnConflict);
catch (ChangeConflictException e)
foreach (ObjectChangeConflict occ in this.ChangeConflicts)
//Keep current values that have changed,
//updates other values with database values
occ.Resolve(RefreshMode.KeepChanges);
public void SubmitOverwrite()
try
this.SubmitChanges(ConflictMode.ContinueOnConflict);
catch (ChangeConflictException e)
foreach (ObjectChangeConflict occ in this.ChangeConflicts)
// All database values overwrite current values with
//values from database
occ.Resolve(RefreshMode.OverwriteCurrentValues);
public void SubmitKeepCurrent()
try
this.SubmitChanges(ConflictMode.ContinueOnConflict);
catch (ChangeConflictException e)
foreach (ObjectChangeConflict occ in this.ChangeConflicts)
//Swap the original values with the values retrieved from the database. No current value is modified
occ.Resolve(RefreshMode.KeepCurrentValues);
【讨论】:
您需要使用 System.Data.Linq 和 System.Data.Linq.Mapping。 @vzczc,你就是那个男人。几天来,我一直在为如何处理这些异常而苦苦挣扎,这帮助我完成了我需要做的事情,因为在某些情况下,我希望客户的数据覆盖数据库中的值。 这是我第一次使用 Linq,如果我能从头再来,我不会在应用程序的生命周期内让数据上下文保持打开状态。【参考方案3】:我在与错误消息描述完全无关的情况下收到此错误。
我所做的是通过一个 DataContext 加载一个 LINQ 对象,然后尝试通过另一个 DataContext 为该对象执行 SubmitChanges() - 给出了完全相同的错误。
我需要做的是调用 DataContext.Table.Attach(myOldObject),然后调用 SubmitChanges(),就像一个魅力。
值得一看,尤其是如果您认为根本不应该有任何冲突。
【讨论】:
【参考方案4】:当 O/R-Designer 中的列或类型与 SQL 数据库中的列不匹配时,有时也会出现“未找到或更改行”错误,特别是如果一列在 SQL 中可以为空,但在 SQL 中不能为空O/R 设计者。
因此请检查您在 O/R-Designer 中的表映射是否与您的 SQL 数据库匹配!
【讨论】:
【参考方案5】:感谢@vzczc。我发现您提供的示例非常有帮助,但我需要在解决后再次调用 SubmitChanges。这是我修改过的方法-希望对某人有所帮助。
/// <summary>
/// Submits changes and, if there are any conflicts, the database changes are auto-merged for
/// members that client has not modified (client wins, but database changes are preserved if possible)
/// </summary>
public void SubmitKeepChanges()
this.Submit(RefreshMode.KeepChanges);
/// <summary>
/// Submits changes and, if there are any conflicts, simply overwrites what is in the database (client wins).
/// </summary>
public void SubmitOverwriteDatabase()
this.Submit(RefreshMode.KeepCurrentValues);
/// <summary>
/// Submits changes and, if there are any conflicts, all database values overwrite
/// current values (client loses).
/// </summary>
public void SubmitUseDatabase()
this.Submit(RefreshMode.OverwriteCurrentValues);
/// <summary>
/// Submits the changes using the specified refresh mode.
/// </summary>
/// <param name="refreshMode">The refresh mode.</param>
private void Submit(RefreshMode refreshMode)
bool moreToSubmit = true;
do
try
this.SubmitChanges(ConflictMode.ContinueOnConflict);
moreToSubmit = false;
catch (ChangeConflictException)
foreach (ObjectChangeConflict occ in this.ChangeConflicts)
occ.Resolve(refreshMode);
while (moreToSubmit);
【讨论】:
【参考方案6】:行未找到或更改通常是并发问题
如果其他用户正在更改同一记录,则会弹出这些错误,因为该记录已被该其他用户更改。因此,当您想消除这些错误时,您应该在应用程序中处理并发。如果您处理好并发性,您将不会再遇到这些错误。上面的代码示例是一种处理并发错误的方法。缺少的是,如果出现并发错误,您应该在这些方法中放置一个 refresh
变量,因此当 refresh
为 true
时,更新后需要在屏幕上刷新数据,因此您还将看到由另一个用户。
/// <remarks>
/// linq has optimistic concurrency, so objects can be changed by other users, while
/// submitted keep database changes but make sure users changes are also submitted
/// and refreshed with the changes already made by other users.
/// </remarks>
/// <returns>return if a refresh is needed.</returns>
public bool SubmitKeepChanges()
// try to submit changes to the database.
bool refresh = false;
try
base.SubmitChanges(ConflictMode.ContinueOnConflict);
/*
* assume a "row not found or changed" exception, if thats the case:
* - keep the database changes already made by other users and make sure
* - this users changes are also written to the database
*/
catch (ChangeConflictException)
// show where the conflicts are in debug mode
ShowConflicts();
// get database values and combine with user changes
base.ChangeConflicts.ResolveAll(RefreshMode.KeepChanges);
// submit those combined changes again to the database.
base.SubmitChanges();
// a refresh is needed
refresh = true;
// return if a refresh is needed.
return refresh;
【讨论】:
【参考方案7】:“还有没有办法让 LINQ 忽略该问题并简单地提交数据?”
您可以将实体上的“更新检查”属性设置为“从不”,以停止将该字段用于乐观并发检查。
你也可以使用:
db.SubmitChanges(ConflictMode.ContinueOnConflict)
【讨论】:
ContinueOnConflict 不会让 LINQ 忽略它。它会让它继续,抛出所有异常,然后将其全部回滚。以上是关于LINQ 中的数据冲突的主要内容,如果未能解决你的问题,请参考以下文章