SQL Server Compact Edition 4 - AccessViolationExce

2019-04-30 05:39发布

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

2条回答
祖国的老花朵
2楼-- · 2019-04-30 06:14

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.

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

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.

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

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..

private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext();

I'd welcome comments on these points..

查看更多
Juvenile、少年°
3楼-- · 2019-04-30 06:22

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

查看更多
登录 后发表回答