SQL Server Compact Edition 4 - AccessViolationException SQL Server Compact Edition 4 - 访问冲突异常

5

我正在使用Entity Framework Code First和SQL Server Compact 4.0构建一个.NET 4 WPF应用程序。 我尝试在后台线程上调用DbContext.SaveChanges()以避免阻塞UI,但我偶尔会出现以下异常:

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)

这是调用 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个回答

3

这里的问题不是序列化访问DbContext对象,而是避免不同线程访问同一对象。因此,解决方案是确保每次需要与数据库交互时都创建一个新的DbContext对象。

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

我不太确定的是如何更新用户界面。如果上述代码在后台线程中运行,并且UI之前已经绑定到LogSubscription.Log集合,则UI线程正在引用集合的另一个实例,您还需要将新条目添加到该集合中。

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

另一个复杂情况是延迟加载,其中实体可能在用户通过UI访问它们之前不会从数据库中加载。为了处理这种情况,似乎必须在UI线程的生命周期内至少维护对DbContext的一个引用。
private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext();

我很乐意针对以下几点进行评论。

我并没有比我的答案更好的理解。您可以在AddNewLogEntry方法中看到该项被添加到两个集合中,一次是上下文集合,一次是“UI集合”。 - Tom Hunter
我曾经遇到过同样的问题,最终发现是BackgroundWorker在其他进程使用上下文的同时尝试访问它。我将该调用移动到其他进程完成后,问题就解决了。我想排队也可以解决这个问题。据我所知,EF应该管理线程,直到我创建后台调用之前,我从未遇到过这样的问题。 - Hannish

0

谢谢,我已经阅读了那篇文章。我正在使用SQL CE 4.0,并且正如文章所述,“应用程序应该对这些对象进行序列化访问”,我已经使用了lock来对SaveChanges()的调用进行序列化处理。 - Tom Hunter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接