Pregunta

Al realizar cambios usando SubmitChanges(), LINQ a veces muere con un ChangeConflictException excepción con el mensaje de error Row not found or changed, sin ninguna indicación ni de la fila que tiene el conflicto ni de los campos con cambios que están en conflicto, cuando otro usuario ha cambiado algún dato en esa fila.

¿Hay alguna manera de determinar qué fila tiene un conflicto y en qué campos ocurre, y también hay una manera de hacer que LINQ ignore el problema y simplemente confirme los datos de todos modos?

Además, ¿alguien sabe si esta excepción ocurre cuando cualquier ¿Los datos de la fila han cambiado, o solo cuando se han cambiado los datos en un campo que LINQ está intentando modificar?

¿Fue útil?

Solución

A continuación se muestra una manera de ver dónde están los conflictos (este es un ejemplo de MSDN, por lo que deberá personalizarlo 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 que ignore el problema y se comprometa de todos modos:

db.SubmitChanges(ConflictMode.ContinueOnConflict);

Otros consejos

Estos (que podrías agregar en una clase parcial a tu contexto de datos podrían ayudarte 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);
        }
    }
}

Recibí este error en una circunstancia que no tiene ninguna relación con lo que describe el mensaje de error.

Lo que hice fue cargar un objeto LINQ a través de un DataContext y luego intenté SubmitChanges() para el objeto a través de un DataContext diferente; obtuve exactamente el mismo error.

Lo que tuve que hacer fue llamar a DataContext.Table.Attach(myOldObject), y luego llamar a SubmitChanges(), funcionó de maravilla.

Vale la pena echarle un vistazo, especialmente si cree que realmente no debería haber ningún conflicto.

El error "Fila no encontrada o modificada" también aparecerá a veces cuando las columnas o tipos en O/R-Designer no coinciden con las columnas en la base de datos SQL, especialmente si una columna es NULLable en SQL pero no anulable en O/ R-Diseñador.

¡Así que verifique si el mapeo de su tabla en O/R-Designer coincide con su base de datos SQL!

Gracias a @vzczc.El ejemplo que diste me pareció muy útil, pero 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);

    }

La fila no encontrada o modificada es la mayoría de las veces un problema de concurrencia.

Si un usuario diferente está cambiando el mismo registro, esos errores aparecen porque ese otro usuario ya cambió el registro.Entonces, cuando desee eliminar esos errores, debe controlar la concurrencia en su aplicación.Si maneja bien la concurrencia, ya no obtendrá estos errores.Los ejemplos de código anteriores son una forma de manejar errores de concurrencia.Lo que falta es que en caso de un error de concurrencia deberías poner un refresh variable en esos métodos, por lo que cuando refresh es true los datos deben actualizarse en la pantalla después de la actualización para que también vea la actualización realizada por el otro usuario.

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

"¿Y también hay alguna manera de hacer que LINQ ignore el problema y simplemente confirme los datos de todos modos?"

Puede configurar la propiedad 'Actualizar verificación' en su entidad en 'Nunca' para evitar que ese campo se utilice para la verificación de simultaneidad optimista.

También puedes usar:

db.SubmitChanges(ConflictMode.ContinueOnConflict)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top