Конфликт данных в LINQ - PullRequest
       17

Конфликт данных в LINQ

19 голосов
/ 28 августа 2008

При внесении изменений с использованием SubmitChanges() LINQ иногда умирает с исключением ChangeConflictException с сообщением об ошибке Row not found or changed, без какого-либо указания на строку, которая имеет конфликт, или на поля с изменениями, которые находятся в конфликте, когда другой пользователь изменил некоторые данные в этой строке.

Есть ли способ определить, в какой строке есть конфликт и в каких полях они встречаются, а также есть ли способ заставить LINQ игнорировать проблему и просто зафиксировать данные независимо от этого?

Кроме того, кто-нибудь знает, возникает ли это исключение, когда любые данные в строке изменились, или только когда данные были изменены в поле, которое LINQ пытается изменить?

Ответы [ 7 ]

24 голосов
/ 28 августа 2008

Вот способ узнать, где находятся конфликты (это пример 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);
19 голосов
/ 28 августа 2008

Эти (которые вы можете добавить в частичном классе к вашему тексту данных, могут помочь вам понять, как это работает:

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);
        }
    }
}
4 голосов
/ 28 августа 2008

Я получил эту ошибку в обстоятельствах, совершенно не связанных с тем, что описывает сообщение об ошибке.

Что я сделал, так это загрузил объект LINQ через один DataContext, а затем попытался SubmitChanges () для объекта через другой DataContext - выдал точно такую ​​же ошибку.

Мне нужно было вызвать DataContext.Table.Attach (myOldObject), а затем вызвать SubmitChanges ().

Стоит посмотреть, особенно если вы считаете, что на самом деле не должно быть никаких конфликтов.

2 голосов
/ 19 сентября 2012

Благодаря @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);

    }
2 голосов
/ 17 сентября 2008

Ошибка «Строка не найдена или изменена» также иногда появляется, когда столбцы или типы в O / R-Designer не совпадают со столбцами в базе данных SQL, особенно если один столбец является пустым в SQL, но не может быть пустым в O / R-Designer.

Так что проверьте, соответствует ли ваше отображение таблиц в O / R-Designer вашей базе данных SQL!

0 голосов
/ 28 февраля 2019

строка не найдена или изменена - чаще всего проблема параллелизма

Если другой пользователь изменяет ту же запись, всплывают эти ошибки, потому что запись уже изменена другим пользователем. Поэтому, когда вы хотите устранить эти ошибки, вы должны обрабатывать параллелизм в вашем приложении. Если вы хорошо справитесь с параллелизмом, вы больше не будете получать эти ошибки. Приведенные выше примеры кода являются способом обработки ошибок параллелизма. Чего не хватает, так это в случае ошибки параллелизма, вы должны поместить переменную 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;
    }
0 голосов
/ 29 августа 2008

"а также есть ли способ заставить LINQ игнорировать проблему и просто зафиксировать данные независимо?"

Вы можете установить для свойства «Проверка обновления» в своей сущности значение «Никогда», чтобы не использовать это поле для оптимистической проверки параллелизма.

Вы также можете использовать:

db.SubmitChanges(ConflictMode.ContinueOnConflict)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...