5

Sto costruendo un'applicazione .NET 4 WPF utilizzando il codice Entity Framework e SQL Server Compact 4.0. Sto cercando di chiamare DbContext.SaveChanges() su un thread in background per evitare di bloccare l'interfaccia utente, ma sto ricevendo di tanto in tanto la seguente eccezione:SQL Server Compact Edition 4 - AccessViolationException

System.AccessViolationException occurred 
    Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt. 
    Source=System.Data.SqlServerCe 
    StackTrace: 
     at System.Data.SqlServerCe.NativeMethodsHelper.OpenStore(IntPtr pOpenInfo, IntPtr pfnOnFlushFailure, IntPtr& pStoreService, IntPtr& pStoreServer, IntPtr& pQpServices, IntPtr& pSeStore, IntPtr& pTx, IntPtr& pQpDatabase, IntPtr& pQpSession, IntPtr& pStoreEvents, IntPtr& pError) 
     at System.Data.SqlServerCe.NativeMethods.OpenStore(IntPtr pOpenInfo, IntPtr pfnOnFlushFailure, IntPtr& pStoreService, IntPtr& pStoreServer, IntPtr& pQpServices, IntPtr& pSeStore, IntPtr& pTx, IntPtr& pQpDatabase, IntPtr& pQpSession, IntPtr& pStoreEvents, IntPtr& pError) 
     at System.Data.SqlServerCe.SqlCeConnection.Open(Boolean silent) 
     at System.Data.SqlServerCe.SqlCeConnection.Open() 
     at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure) 
     at System.Data.EntityClient.EntityConnection.Open() 
     at System.Data.Objects.ObjectContext.EnsureConnection() 
     at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options) 
     at System.Data.Entity.Internal.InternalContext.SaveChanges() 
     at System.Data.Entity.Internal.LazyInternalContext.SaveChanges() 
     at System.Data.Entity.DbContext.SaveChanges() 
     at SourceLog.Model.LogSubscriptionManager.<SaveChanges>b__2() in C:\github.com\tomhunter-gh\SourceLog\SourceLog.Model\LogSubscriptionManager.cs:line 51 
    InnerException: (null) 

Ecco il codice che chiama SaveChanges():

internal static readonly object DbSaveLockObject = new object(); 
public static void SaveChanges() 
{ 
    Task.Factory.StartNew(() => 
    { 
     lock (DbSaveLockObject) 
     { 
      Debug.WriteLine(DateTime.Now + ": SaveChanges in lock"); 
      Db.SaveChanges(); 
     } 
    }); 
} 

risposta

2

Il problema qui non è serializzazione accesso all'oggetto DbContext, è evitare l'accesso allo stesso oggetto da diversi thread. Quindi la soluzione è assicurarsi di creare un nuovo oggetto DbContext ogni volta che è necessario interagire con il database.

using (var db = new SourceLogContext()) 
{ 
    db.LogSubscriptions.First(s => s.LogSubscriptionId == LogSubscriptionId) 
     .Log.Add((LogEntry)e.LogEntry); 
    db.SaveChanges(); 
} 

Quello di cui non sono abbastanza sicuro è il modo in cui gestisci l'aggiornamento dell'interfaccia utente. Se il codice sopra è in esecuzione in un thread in background e l'interfaccia utente è stata precedentemente associata alla raccolta LogSubscription.Log, il thread dell'interfaccia utente fa riferimento a una diversa istanza della raccolta e devi aggiungere anche la nuova voce a questa raccolta.

_uiThread.Post(entry => Log.Add((LogEntry)entry), e.LogEntry); 

Un'ulteriore complicazione è lazy loading cui entità non possono essere caricati dal database finché l'utente non ha accedervi tramite l'interfaccia utente. Per gestire questo sembra si deve mantenere almeno un riferimento alla DbContext per la vita del thread UI ..

private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext(); 

avrei news anche su questi punti ..

+2

Hai mai risolto il problema? Sto avendo un problema simile. –

+0

Non ho avuto una comprensione migliore di quella che ho nella mia risposta. Puoi vedere l'elemento aggiunto a due raccolte nel metodo [AddNewLogEntry] (https://github.com/tomhunter-gh/SourceLog/blob/aed3718af18fcff471f04c83f83a0160b97b6829/SourceLog.Model/LogSubscription.cs#L90), una volta al contesto collezione e una volta alla "collezione UI". –

+0

Ho avuto lo stesso problema, è finito per essere un backgroundworker che cercava di accedere al contesto mentre altri processi lo stavano usando. Ho spostato quella chiamata dopo che gli altri processi sono terminati e questo l'ha risolto. Immagino che anche loro possano fare la fila. Per quanto ne so EF dovrebbe gestire il threading, non ho mai avuto un problema fino a quando non ho creato la chiamata in background. – Hannish

0

Un AccessViolationException si verifica solo quando il codice gestito verificabile interagisce con codice non gestito o con codice gestito non sicuro.

Si dovrebbe passare attraverso questo blog le mie MS su come risolvere l'accesso voilation error: http://blogs.msdn.com/b/sqlservercompact/archive/2009/05/06/troubleshooting-access-violation-exception-while-using-sql-server-compact-database-with-ado-net-provider.aspx

+0

Grazie, ho leggi quell'articolo Sto usando SQL CE 4.0 e come afferma l'articolo "l'applicazione dovrebbe serializzare l'accesso a questi oggetti" Ho usato 'lock' per serializzare le chiamate a' SaveChanges() '.. –

+0

(ma ottengo ancora l'errore. .) –