76 messaggi dal 16 marzo 2011
Salve

ho il seguente problema, ho un db modellato mediante modello edmx, ed ho due tabelle (Tabella A e Tabella B) relazionate mediante la chiave primaria ID, quindi ID è PK su entrambe e la relazione sarà PK_ID_TabellaA_FK_ID_TabellaB.

A questo punto per fare l'update seleziono un oggetto dalla Tabella A, lo modifico e faccio SaveChanges.

Anche se il saveChange non ha successo il modello rimane comunque modificato anche se esco dal metodo. Sapete dirmi perchè?
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

vale820_1 ha scritto:
Anche se il saveChange non ha successo il modello rimane comunque modificato anche se esco dal metodo. Sapete dirmi perchè?

Perché così hai l'opportunità di prendere delle contromisure.
Il SaveChanges è fallito perché il database era temporaneamente non disponibile? Posso riprovare ad eseguire di nuovo il SaveChanges dopo una breve attesa.
E' fallito per un problema di concorrenza ottimistica? Posso rileggermi i cambiamenti e riproporli all'utente insieme ad una richiesta di conferma per forzarne l'aggiornamento.

Se invece desideri che i cambiamenti vadano persi, allora puoi mettere un try...catch attorno al SaveChanges (per evitare che sollevi un'eccezione) e poi aggiornare le entità presenti nel contesto rileggendole dal database. Il metodo per farlo è:
contesto.Refresh(RefreshMode.StoreWins, entità);


ciao
Modificato da BrightSoul il 20 novembre 2012 22.46 -

Enjoy learning and just keep making
76 messaggi dal 16 marzo 2011
Purtroppo anche con l'istruzione che mi gai consigliato l'effetto lo ho lo stesso, hai altre idee?

Tra l'altro ho visto un altra cosa, se tento di inserire un oggetto che c'è già sulla tabella A giustamente al SaveChages ho un eccezione, il problema è che se poi prelevo l'oggetto che c'era su Db tramite una select, lo modifico e tento di aggiornarlo mi continua a dare eccezione per conflitto tra chiave primaria, è come se tentasse di reinserirlo invece che modificarlo...

Credo che l'objectContext entry in una sorta di stato di inconsistenza, o comunque che rimanga all'ultima operazione effettuata, il problema che mi pongo, all'inserimento errato l'object context non dovrebbe dare errore prima del SaveChange?

Grazie mille
Modificato da vale820_1 il 21 novembre 2012 12.37 -
76 messaggi dal 16 marzo 2011
Ho trovato una soluzione, basta gestire l'eccezione del SaveChanges, e aggiungere il seguente codice

var objectStateEntries = this.context
.ObjectStateManager
.GetObjectStateEntries(EntityState.Modified);

foreach (var objectStateEntry in objectStateEntries)
{
this.context.Detach(objectStateEntry.Entity);
}

dove EntityState sarà Added per l'insert, Deleted per la Delete, e Modified per l'update.

C'è solo un piccolo problema. Dato che cancella tutte le Entity modificate, se si aggiunge una riga ok, ma se si modifica, diventa un problema xchè cancella tutte le righe, avete una soluzione?
11.886 messaggi dal 09 febbraio 2002
Contributi
ciao,

vale820_1 ha scritto:

il problema che mi pongo, all'inserimento errato l'object context non dovrebbe dare errore prima del SaveChange?

Dunque, quando usi il metodo .AddObject per aggiungere un'entità, essa vive nel contesto con lo stato di Added.
Fin qui, l'ObjectContext non si preoccupa di andare a verificare nel database se esiste già un record con quella chiave e per questo motivo ti lascia compiere quell'operazione senza errori.
Quando vai a selezionare dal database il record che porta la stessa chiave dell'oggetto Added, ecco che si crea un problema perché Entity Framework non può materializzare un oggetto che porta la stessa chiave di un altro oggetto già esistente nel contesto. Violerebbe il principio dell'Identity Map e così preferisce invece darti un'eccezione.
Non può neanche riciclare l'oggetto che avevi aggiunto tu, perché quell'oggetto si trova sullo stato di Added. Non ho controllato il funzionamento interno di Entity Framework ma immagino che se ti dà un errore è perché non vuole modificare arbitrariamente lo stato degli oggetti, che così facendo passerebbe da Added a Unchanged.

Quindi, siccome hai introdotto tu l'inconsistenza, in primo luogo, aggiungendo un'entità che era già presente nel database, poi devi fare qualcosa per compensare.

Quando il SaveChanges ti solleva la UpdateException, hai l'opportunità di sapere quali erano le entità causa del problema dalla sua proprietà Entries.
Enumera le entries e, se lo State era Added, ne fai il Detach (se vuoi rimuoverla dal contesto) o ne cambi lo stato a Modified e poi ne fai il Refresh (se vuoi che sia aggiornata).
Se lo State era Modified (e probabilmente la UpdateException era di tipo OptimisticConcurrencyException), allora devi solo fare il Refresh dell'entità.
Dunque prova:
try
{
    //SaveChanges solleverà un'eccezione
    context.SaveChanges();
}
catch (UpdateException exc)
{
    //enumero le StateEntries responsabili dell'eccezione
    foreach (var entry in exc.StateEntries)
    {
        switch (entry.State)
        {
            case EntityState.Added:
                //compenso modificando lo stato dell'entità su Modified
                entry.ChangeState(EntityState.Modified);
                //Io qui faccio prevalere le modifiche del client ma sarebbe il caso di informare l'utente che
                //l'entità che ha cercato di inserire già esisteva.
                context.Refresh(RefreshMode.ClientWins, entry.Entity);
                break;
            
            case EntityState.Modified:
                context.Refresh(RefreshMode.ClientWins, entry.Entity);
                break;

            default:
                throw new NotSupportedException("Gli altri casi non sono supportati");
        }
    }
    //riprovo a salvare
    context.SaveChanges();
}


ciao
Modificato da BrightSoul il 21 novembre 2012 20.51 -

Enjoy learning and just keep making
76 messaggi dal 16 marzo 2011
Grazie per l'ottima idea, solo che dentro il cath l'ho dovuto modificare così perchè altrimenti in alcuni casi mi dava eccezione in altri non funzionava:

 foreach (var objectStateEntry in exc.StateEntries)
                    {
                        switch (objectStateEntry.State)
                        {
                            case EntityState.Added:
                                this.context.Detach(objectStateEntry.Entity);                               
                                break;
                            case EntityState.Modified:
                                this.context.ObjectStateManager.ChangeObjectState(objectStateEntry.Entity, System.Data.EntityState.Unchanged); 
                                break;

                            default:
                                throw new NotSupportedException("Gli altri casi non sono supportati");
                        }
                    }
2 messaggi dal 03 gennaio 2008
Aiuto, credo di essere nella stessa situazione, ma ne la soluzione proposta da te, ne quella ribattuta dall'altro utente sembra avere effetto! Continuo a ricevere l'errore per cui esiste già nel contesto una entity con la stessa chiave! L'operazione è un'aggiornamento di una entity con all'interno una navigation property ad un'altra entity che, per prova, è stata manualmente modifica nel db tra una richiesta e l'altra. Il codice:

 Protected Sub SalvaSchedaContatto()
        Dim script As String = ""
        If Page.IsValid Then
            Dim cM As ContattoManager
            Dim pM As New PersonaManager
            Dim result As Boolean
            Dim saveChanges As Integer = 0
            Try
                Using New UnitOfWorkScope(True)
                    cM = New ContattoManager
                    If Not Session("ResolveUpdateException_" & _c.Id) Is Nothing Then

                        result = cM.Update(_c)
                        TrasferisciRecapiti()
                        Dim oContext As System.Data.Objects.ObjectContext = DirectCast(UnitOfWorkScope.CurrentObjectContext, IObjectContextAdapter).ObjectContext
                        Dim refreshMode As Objects.RefreshMode = CType(Session("RefreshMode_" & _c.Id), System.Data.Objects.RefreshMode)

                        Try
                          
                            oContext.SaveChanges()

                        Catch ex1 As UpdateException
                            For Each entry In ex1.StateEntries
                                Select Case entry.State
                                    Case EntityState.Added
                                        entry.ChangeState(EntityState.Modified)
                                        oContext.Refresh(refreshMode, entry.Entity)
                                    Case EntityState.Modified                                        
                                        oContext.Refresh(refreshMode, entry.Entity)
                                End Select
                            Next

                            Session("ExceptionResolved_" & _c.Id) = True
                            Session("ResolveUpdateException_" & _c.Id) = Nothing
                        Catch ex As Exception
                            If ex.GetType IsNot GetType(NotSupportedException) Then
                                Throw ex
                            End If

                        End Try

                    Else
                        result = cM.Update(_c)
                        TrasferisciRecapiti()

                    End If
                End Using

              
                ClearSession()

                If Session("ResolveUpdateException_" & _c.Id) Is Nothing Then
                    If Not result Then
                        script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-alert' style='display: inline-block'></span> Spiacenti: non hai l\'autorizzazione per apportare modifiche a questa scheda contatto." & ControlChars.Quote & _
                        ", dismissQueue: true, force: true, timeout: 3000, modal: true, layout: " & ControlChars.Quote & "center" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "warning" & _
                        ControlChars.Quote & "});"
                        ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-salvataggio", script, True)
                    Else
                        If Not Session("Recapiti_" & _c.Id) Is Nothing Then
                            For Each r As Recapito In CType(Session("Recapiti_" & _c.Id), ICollection(Of Recapito))
                                r.IsInSession = False
                            Next
                        End If

                        If Not Session("Indirizzi_" & _c.Id) Is Nothing Then
                            For Each i As Indirizzo In CType(Session("Indirizzi_" & _c.Id), ICollection(Of Indirizzo))
                                i.IsInSession = False
                            Next
                        End If

                        Dim script1 As String = "$('.ui-state-highlight').removeClass('ui-state-highlight'); $('.ui-state-error').css({ display : 'none'});"

                        script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-check' style='display: inline-block'></span> Salvataggio informazioni aggiornate avvenuto con successo!" & ControlChars.Quote & _
                    ", dismissQueue: true, force: true, timeout: 3000, modal: false, layout: " & ControlChars.Quote & "bottomLeft" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "success" & _
                    ControlChars.Quote & "});"
                        ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "conferma-salvataggio", script, True)
                        ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "rimuovi-stile", script1, True)
                    End If
                Else
                    Session("ExceptionResolved_" & _c.Id) = Nothing
                    Session("ResolveUpdateException_" & _c.Id) = Nothing

                    If DirectCast(Session("RefreshMode_" & _c.Id), Objects.RefreshMode) = Objects.RefreshMode.ClientWins Then
                        script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-notice' style='display: inline-block'></span>Salvataggio scheda avvenuto.<br /><strong>La scheda è stata aggiornata coi valori da te attualmente introdotti.</strong>" & ControlChars.Quote & _
                         ", dismissQueue: true, force: true, timeout: false, modal: true, layout: " & ControlChars.Quote & "center" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "success" & _
                            ControlChars.Quote & "});"
                        ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "conferma-overwrite", script, True)
                    Else
                        script = "javascript:ConfermaDiscardUpdtes();"
                        ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-discard", script, True)
                    End If
                    Session("ResolveUpdateException_" & _c.Id) = Nothing
                    Session("RefreshMode_" & _c.Id) = Nothing
                End If                
               
            Catch ex As DbEntityValidationException

                script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-alert' style='display: inline-block'></span> Si sono verificati dei problemi durante il salvataggio. <br >Di seguito " & _
                              "è riportata la descrizione dell'errore: " & DirectCast(ex, DbEntityValidationException).EntityValidationErrors.First.ValidationErrors.FirstOrDefault.ErrorMessage.ToString & ControlChars.Quote & _
                              ", dismissQueue: true, force: true, timeout: false, modal: true, layout: " & ControlChars.Quote & "center" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "error" & _
                              ControlChars.Quote & "});"
                ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-salvataggio", script, True)                           

            Catch ex As DbUpdateConcurrencyException
                If Session("ExceptionResolved_" & _c.Id) Is Nothing Then
                    Session("Entries_" & _c.Id) = ex
                    script = "javascript:RichiestaConfermaConcorrenzaViolata();"
                    ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-concorrenza", script, True)
                End If
               

            Catch ex As Exception
                script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-alert' style='display: inline-block'></span> Si sono verificati dei problemi durante il salvataggio. <br >Di seguito " & _
                    "è riportata la descrizione dell'errore: " & ex.Message.ToString & ControlChars.Quote & _
                    ", dismissQueue: true, force: true, timeout: false, modal: true, layout: " & ControlChars.Quote & "center" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "error" & _
                    ControlChars.Quote & "});"
                ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-salvataggio", script, True)

            Finally
                cM = Nothing               
            End Try
        Else

            script = "var conferma = noty({text:" & ControlChars.Quote & "<span class='ui-icon ui-icon-alert' style='display: inline-block'></span>Sembrano esserci degli errori di validazione nei campi del modulo." & ControlChars.Quote & _
                  ", dismissQueue: true, force: true, timeout: 3000, modal: true, layout: " & ControlChars.Quote & "center" & ControlChars.Quote & ", theme: 'defaultTheme', type:" & ControlChars.Quote & "warning" & _
                  ControlChars.Quote & "});"
            ScriptManager.RegisterStartupScript(Me.clientScriptContainer, Me.GetType(), "errore-salvataggio", script, True)

        End If
    End Sub


L'idea è quella di notificare all'utente il problema di concorrenza mediante uno strumento jquery e una volta impostata la scelta del refresh mode chiamarlo sul pezzo di routine che gestisce il salvataggio per chiamare il refresh del context...

Aiutooo.....

Daniel
76 messaggi dal 16 marzo 2011
Ciao

l'unico aiuto che sò darti è quello che ho messo nel catch :

catch (UpdateException exc)
{
// Gestione dell'eccezione, si torna indietro rispetto alla modifica fatta sul
// context se il SaveChanges non è andato a buon fine
foreach (var objectStateEntry in exc.StateEntries)
{
switch (objectStateEntry.State)
{
case EntityState.Added:
this.context.Detach(objectStateEntry.Entity);
break;
case EntityState.Modified:
toSobstitute = toRestore;

this.context.Refresh(RefreshMode.StoreWins, objectStateEntry.Entity);
break;

default:
throw new NotSupportedException("Gli altri casi non sono supportati");
}
}
return new Result(ResultType.Exception,exc.ToString());
}

Purtroppo di Jquery non sono pratica, spero di esserti stata d'aiuto

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.