Конфликт данных в LINQ
При внесении изменений с использованием 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);
Ответ 2
Эти (которые вы могли бы добавить в частичный класс к вашему 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);
}
}
}
Ответ 3
Я получил эту ошибку в обстоятельствах, совершенно не связанных с тем, что описывает сообщение об ошибке.
То, что я сделал, это загрузить объект LINQ через один DataContext, а затем попытался передать SubmitChanges() для объекта через другой DataContext - дал эту ту же ошибку.
Мне нужно было вызвать DataContext.Table.Attach(myOldObject), а затем вызвать SubmitChanges(), работая как шарм.
Стоит посмотреть, особенно если вы считаете, что конфликтов действительно не должно быть.
Ответ 4
Ошибка "Строка не найдена или изменена" также иногда появляется, когда столбцы или типы в O/R-Designer не соответствуют столбцам в базе данных SQL, особенно если один столбец имеет NULLable в SQL, но не имеет значения NULL O/R-Designer.
Итак, проверьте, соответствует ли ваше сопоставление таблиц в 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
Те методы не завершены? Не хотите ли вы повторить отправку или обернуть ее в какой-то цикл, иначе не останется гарантии, что изменения будут устранены при конфликте?
Ответ 7
", а также есть способ заставить LINQ игнорировать проблему и просто зафиксировать данные независимо?"
Вы можете установить свойство "Обновить проверку" на своей сущности на "Никогда", чтобы остановить это поле для оптимизации concurrency.
Вы также можете использовать:
db.SubmitChanges(ConflictMode.ContinueOnConflict)