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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
19
0
9 882
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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

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

Что мне нужно было сделать, так это вызвать DataContext.Table.Attach (myOldObject), а затем вызвать SubmitChanges (), сработало как шарм.

Стоит взглянуть, особенно если вы считаете, что конфликтов вообще не должно быть.

Ответ принят как подходящий

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

Мне придется добавить это в свой код. Я только что вытолкнул приложение, которое столкнулось с исключением «Строка не найдена».

Benjamin Autin 20.12.2008 01:52

Будьте осторожны, ConflictMode.ContinueOnConflict в любом случае не заставляет его фиксироваться, он позволяет ему попытаться выполнить ВСЕ обновления, поэтому вы получите ВСЕ конфликты сразу, а не останетесь на первом, но он все равно откатится.

Daniel Magliola 26.03.2009 17:02

Чтобы он игнорировал проблему, вам нужно вызвать ResolveAll (KeepChanges) для вашего улова {}

Daniel Magliola 26.03.2009 17:03

Черт возьми, как я раньше не знал о свойстве ChangeConflicts ?? О, время и здравомыслие, которое я мог бы сэкономить! АААААА!

Daniel Schaffer 01.02.2011 02:13

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

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.

mattruma 02.12.2008 02:37

@vzczc, ты мужчина. Я много дней боролся с тем, как справиться с этими исключениями, и это помогло мне выполнить именно то, что мне нужно было сделать, поскольку бывают случаи, когда я хочу, чтобы данные клиента перезаписывали значения в БД.

Grasshopper 27.01.2013 22:26

Я впервые использую Linq, и если бы я мог делать это снова и снова, я бы не оставлял контекст данных открытым в течение всего жизненного цикла приложения.

Grasshopper 27.01.2013 22:27

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

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

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

db.SubmitChanges(ConflictMode.ContinueOnConflict)

ContinueOnConflict не заставит LINQ игнорировать его. Он позволит продолжить, выбросит все исключения, а затем откатит все назад.

Daniel Magliola 26.03.2009 17:04

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

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

Спасибо @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);

    }

строка, которая не найдена или не изменена, в большинстве случаев является проблемой параллелизма

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

Другие вопросы по теме