c# - examples - Conflicto de datos en LINQ
linq select (7)
Al realizar cambios utilizando SubmitChanges()
, LINQ a veces muere con una excepción ChangeConflictException
con el mensaje de error Row not found or changed
, sin ninguna indicación de la fila que tiene el conflicto o los campos con cambios que están en conflicto, cuando otro usuario tiene cambiado algunos datos en esa fila.
¿Hay alguna manera de determinar qué fila tiene un conflicto y en qué campos ocurren, y también hay una manera de hacer que LINQ ignore el problema y simplemente confirmar los datos independientemente?
Además, ¿alguien sabe si esta excepción se produce cuando ha cambiado algún dato en la fila, o solo cuando se han modificado los datos en un campo que LINQ está intentando modificar?
"¿Y también hay una forma de lograr que LINQ ignore el problema y simplemente comprometer los datos independientemente?"
Puede establecer la propiedad ''Actualizar verificación'' en su entidad en ''Nunca'' para detener ese campo que se usa para la comprobación de concurrencia optimista.
También puedes usar:
db.SubmitChanges(ConflictMode.ContinueOnConflict)
Aquí hay una forma de ver dónde están los conflictos (este es un ejemplo de MSDN, por lo que deberá personalizar en gran medida):
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);
}
}
}
Para hacer que ignore el problema y se comprometa de todos modos:
db.SubmitChanges(ConflictMode.ContinueOnConflict);
Estos (que podría agregar en una clase parcial a su contexto de datos podrían ayudarlo a comprender cómo funciona esto:
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);
}
}
}
He recibido este error en una circunstancia completamente ajena a lo que describe el mensaje de error.
Lo que hice fue cargar un objeto LINQ a través de un DataContext, y luego traté de SubmitChanges () para el objeto a través de un DataContext diferente; di este mismo error exacto.
Lo que tuve que hacer fue llamar a DataContext.Table.Attach (myOldObject), y luego llamar a SubmitChanges (), funcionó como un amuleto.
Vale la pena mirar, especialmente si eres de la opinión de que realmente no debería haber ningún conflicto.
El error "Fila no encontrada o cambiada" también aparecerá a veces cuando las columnas o los tipos en O / R-Designer no coincidan con las columnas en la base de datos SQL, especialmente si una columna es NULLable en SQL pero no puede contener nulos en O / R-Designer.
¡Entonces verifique si su mapeo de tablas en el O / R-Designer coincide con su base de datos SQL!
Esos métodos no están completos? ¿No le gustaría reintentar el envío o envolverlo en algún tipo de ciclo, de lo contrario no queda garantía de que los cambios se realicen en caso de conflicto?
Gracias a @vzczc. Encontré el ejemplo que me dio muy útil pero que necesitaba llamar a SubmitChanges nuevamente después de resolverlo. Aquí están mis métodos modificados: espero que ayude a alguien.
/// <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);
}