Domanda

Quando si apportano modifiche utilizzando SubmitChanges(), LINQ a volte muore con a ChangeConflictException eccezione con il messaggio di errore Row not found or changed, senza alcuna indicazione della riga in conflitto o dei campi con modifiche in conflitto, quando un altro utente ha modificato alcuni dati in quella riga.

Esiste un modo per determinare quale riga presenta un conflitto e in quali campi si verifica, e c'è anche un modo per fare in modo che LINQ ignori il problema e semplicemente impegni i dati a prescindere?

Inoltre, qualcuno sa se questa eccezione si verifica quando Qualunque i dati nella riga sono cambiati o solo quando i dati sono stati modificati in un campo che LINQ sta tentando di modificare?

È stato utile?

Soluzione

Ecco un modo per vedere dove sono i conflitti (questo è un esempio MSDN, quindi dovrai personalizzarlo pesantemente):

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

Per far sì che ignori il problema e si impegni comunque:

db.SubmitChanges(ConflictMode.ContinueOnConflict);

Altri suggerimenti

Questi (che potresti aggiungere in una classe parziale al tuo contesto dati potrebbero aiutarti a capire come funziona:

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

Ho ricevuto questo errore in una circostanza completamente estranea a ciò che descrive il messaggio di errore.

Ciò che ho fatto è stato caricare un oggetto LINQ tramite un DataContext e quindi provare a SubmitChanges() per l'oggetto tramite un DataContext diverso: ha restituito esattamente lo stesso errore.

Quello che dovevo fare era chiamare DataContext.Table.Attach(myOldObject) e poi chiamare SubmitChanges(), ha funzionato a meraviglia.

Vale la pena dare un'occhiata, soprattutto se sei dell'opinione che in realtà non dovrebbero esserci conflitti.

L'errore "Riga non trovata o modificata" apparirà a volte anche quando le colonne o i tipi nell'O/R-Designer non corrispondono alle colonne nel database SQL, soprattutto se una colonna è NULLable in SQL ma non nullable nell'O/ R-Designer.

Quindi controlla se la mappatura della tua tabella in O/R-Designer corrisponde al tuo database SQL!

Grazie a @vzczc.Ho trovato l'esempio che hai fornito molto utile, ma dovevo richiamare nuovamente SubmitChanges dopo aver risolto.Ecco i miei metodi modificati: spero che aiuti qualcuno.

    /// <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 riga non trovata o modificata è nella maggior parte dei casi un problema di concorrenza

Se un utente diverso sta modificando lo stesso record, verranno visualizzati gli errori perché il record è già stato modificato da quell'altro utente.Pertanto, quando vuoi eliminare questi errori dovresti gestire la concorrenza nella tua applicazione.Se gestisci bene la concorrenza non otterrai più questi errori.Gli esempi di codice riportati sopra rappresentano un modo per gestire gli errori di concorrenza.Ciò che manca è che in caso di errore di concorrenza dovresti inserire un file refresh variabile in questi metodi, quindi quando refresh È true i dati devono essere aggiornati sullo schermo dopo l'aggiornamento in modo da poter vedere anche l'aggiornamento effettuato dall'altro utente.

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

"ed esiste anche un modo per fare in modo che LINQ ignori il problema e semplicemente impegni i dati a prescindere?"

Puoi impostare la proprietà "Verifica aggiornamenti" sulla tua entità su "Mai" per interrompere l'utilizzo del campo per il controllo ottimistico della concorrenza.

Puoi anche usare:

db.SubmitChanges(ConflictMode.ContinueOnConflict)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top