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





Я получил эту ошибку при обстоятельствах, совершенно не связанных с тем, что описано в сообщении об ошибке.
Я загрузил объект 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);
Будьте осторожны, ConflictMode.ContinueOnConflict в любом случае не заставляет его фиксироваться, он позволяет ему попытаться выполнить ВСЕ обновления, поэтому вы получите ВСЕ конфликты сразу, а не останетесь на первом, но он все равно откатится.
Чтобы он игнорировал проблему, вам нужно вызвать ResolveAll (KeepChanges) для вашего улова {}
Черт возьми, как я раньше не знал о свойстве ChangeConflicts ?? О, время и здравомыслие, которое я мог бы сэкономить! АААААА!
Они (которые вы могли бы добавить в частичный класс к вашему тексту данных, могут помочь вам понять, как это работает:
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, и если бы я мог делать это снова и снова, я бы не оставлял контекст данных открытым в течение всего жизненного цикла приложения.
"а также есть ли способ заставить LINQ игнорировать проблему и просто фиксировать данные независимо?"
Вы можете установить свойство «Проверка обновлений» в своей сущности на «Никогда», чтобы остановить использование этого поля для проверки оптимистичного параллелизма.
Вы также можете использовать:
db.SubmitChanges(ConflictMode.ContinueOnConflict)
ContinueOnConflict не заставит LINQ игнорировать его. Он позволит продолжить, выбросит все исключения, а затем откатит все назад.
Ошибка «Строка не найдена или изменена» также иногда появляется, когда столбцы или типы в 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;
}
Мне придется добавить это в свой код. Я только что вытолкнул приложение, которое столкнулось с исключением «Строка не найдена».