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 变量,因此当 refreshtrue 时,更新后需要在屏幕上刷新数据,因此您还将看到由另一个用户。

    /// <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 中的数据冲突的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to SQL语句(13)之开放式并发控制和事务

使用 linq 识别日期冲突

LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别

列车数据库中的 linq 查询

C#中的LINQ 基础

ASP.NET中的Linq怎么用?