5

Estoy construyendo una aplicación .NET 4 WPF utilizando primero el código de Entity Framework y SQL Server Compact 4.0. Estoy intentando llamar DbContext.SaveChanges() en un subproceso de fondo para evitar el bloqueo de la interfaz de usuario, pero estoy en ocasiones conseguir la siguiente excepción: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) 

Aquí está el código que llama 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(); 
     } 
    }); 
} 

Respuesta

2

El tema aquí no es serializar el acceso al objeto DbContext, se evita el acceso al mismo objeto desde diferentes hilos. Entonces, la solución es garantizar que crees un nuevo objeto DbContext cada vez que necesites interactuar con la base de datos.

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

Lo que no estoy seguro es cómo lidiar con la actualización de la IU. Si el código anterior se ejecuta en una secuencia de fondo y la interfaz de usuario se ha vinculado previamente a la colección LogSubscription.Log, entonces el subproceso de la interfaz de usuario hace referencia a una instancia diferente de la colección y también debe agregar la nueva entrada a esta colección.

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

Una complicación adicional es la carga diferida donde las entidades podrían no ser cargados desde la base de datos hasta que el usuario tiene acceso a ellos a través de la interfaz de usuario. Para manejar esto parece que tiene para mantener al menos una referencia a la DbContext para la vida de la rosca de la interfaz de usuario ..

private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext(); 

daría la bienvenida a los comentarios sobre estos puntos ..

+2

¿Alguna vez resolvió el problema? Estoy teniendo un problema similar. –

+0

Realmente no obtuve una mejor comprensión de la que tengo en mi respuesta. Puede ver el elemento que se agrega a dos colecciones en el método [AddNewLogEntry] (https://github.com/tomhunter-gh/SourceLog/blob/aed3718af18fcff471f04c83f83a0160b97b6829/SourceLog.Model/LogSubscription.cs#L90), una vez en el contexto colección y una vez a la "colección de UI". –

+0

Tuve el mismo problema, terminó siendo un backgroundworker tratando de acceder al contexto mientras otros procesos lo estaban usando. Moví esa llamada después de que los otros procesos terminaron y eso lo resolvió. Supongo que eso también funcionaría. Por lo que sé que EF debe gestionar el enhebrado, nunca tuve un problema así hasta que haya creado la llamada de fondo. – Hannish

0

Un AccessViolationException ocurre solo cuando el código administrado verificable interactúa con un código no administrado o con un código administrado inseguro.

Hay que ir a través de este blog Mi MS sobre cómo solucionar problemas de acceso 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

Gracias, no tengo lee ese articulo Estoy usando SQL CE 4.0 y como el artículo dice "la aplicación debe serializar el acceso a estos objetos" He usado 'lock' para serializar las llamadas a' SaveChanges() '.. –

+0

(pero sigo recibiendo el error. .) –

Cuestiones relacionadas