【问题标题】:SQL Server Compact Edition 4 - AccessViolationExceptionSQL Server Compact Edition 4 - AccessViolationException
【发布时间】:2012-08-10 05:53:44
【问题描述】:

我首先使用实体​​框架代码和 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();
        }
    });
}

【问题讨论】:

    标签: c# multithreading entity-framework sql-server-ce sql-server-ce-4


    【解决方案1】:

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

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

    我不太确定的是您如何处理更新 UI。如果上面的代码在后台线程中运行,并且 UI 之前已绑定到 LogSubscription.Log 集合,则 UI 线程正在引用该集合的不同实例,您还必须将新条目添加到该集合中。

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

    更复杂的是延迟加载,在用户通过 UI 访问实体之前,可能不会从数据库加载实体。要处理此问题,您似乎必须在 UI 线程的生命周期内至少维护一个对 DbContext 的引用。

    private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext();
    

    在这些方面我欢迎 cmets..

    【讨论】:

    • 我并没有比我在回答中得到的更好的理解。您可以在AddNewLogEntry 方法中看到该项目被添加到两个集合中,一次添加到上下文集合,一次添加到“UI 集合”。
    • 我遇到了同样的问题,它最终是一个后台工作人员试图访问上下文,而其他进程正在使用它。在其他进程完成并解决它之后,我移动了那个调用。我想排队他们也可以。据我所知 EF 应该管理线程,在创建后台调用之前我从未遇到过这样的问题。
    【解决方案2】:

    仅当可验证的托管代码与非托管代码或不安全的托管代码交互时,才会发生 AccessViolationException。

    您应该通过我的 MS 博客了解如何解决访问违规问题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

    【讨论】:

    • 谢谢,我读过那篇文章。我正在使用 SQL CE 4.0,正如文章所述“应用程序应该序列化对这些对象的访问”,我使用 lock 序列化对 SaveChanges() 的调用..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多