Вопрос

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

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

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

Это было полезно?

Решение

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

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

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

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);
        }
    }
}

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

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

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

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

Ошибка "Строка не найдена или не изменена" также иногда появляется, когда столбцы или типы в 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;
    }

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

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

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

db.SubmitChanges(ConflictMode.ContinueOnConflict)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top