I'm building a .NET 4 WPF application using Entity Framework code first and SQL Server Compact 4.0. I'm trying to call DbContext.SaveChanges()
on a background thread to avoid blocking the UI, but I'm occasionally getting the following exception:
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)
Here's the code that calls 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();
}
});
}
The issue here is not serializing access to the DbContext object, it's avoiding access to the same object from different threads. So the solution is to ensure you create a new DbContext object every time you need to interact with the database.
What I'm not quite sure about is how you deal with updating the UI. If the code above is running in a background thread and the UI has previously been bound to the LogSubscription.Log collection, then the UI thread is referencing a different instance of the collection and you have to add the new entry to this collection as well.
A further complication is lazy loading where entities might not be loaded from the database until the user has access them through the UI. To handle this it seems you have to maintain at least one reference to the DbContext for the life of the UI thread..
I'd welcome comments on these points..
An AccessViolationException occurs only when verifiable managed code interacts with unmanaged code or with unsafe managed code.
You should go through this blog my MS on how to troubleshoot access 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