5

Buduję aplikację .NET 4 WPF przy użyciu kodu Entity Framework first i SQL Server Compact 4.0. Próbuję zadzwonić DbContext.SaveChanges() na wątek tła, aby uniknąć blokowania UI, ale ja od czasu do czasu otrzymuję następujący wyjątek: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) 

Oto kod, który wywołuje 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(); 
     } 
    }); 
} 

Odpowiedz

2

Chodzi tu nie szeregowania dostępu do obiektu DbContext, to unikanie dostęp do tego samego obiektu z różnych wątków. Rozwiązaniem jest zatem zapewnienie tworzenia nowego obiektu DbContext za każdym razem, gdy zachodzi potrzeba interakcji z bazą danych.

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

Nie jestem do końca pewien, w jaki sposób radzisz sobie z aktualizowaniem interfejsu użytkownika. Jeśli powyższy kod działa w wątku w tle, a interfejs użytkownika był wcześniej powiązany z kolekcją LogSubscription.Log, wątek interfejsu użytkownika odwołuje się do innej instancji kolekcji i musisz dodać nowy wpis również do tej kolekcji.

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

Dodatkową komplikacją jest leniwy załadunku w przypadku gdy podmioty nie mogą być ładowane z bazy danych, dopóki użytkownik ma do nich dostęp za pośrednictwem interfejsu użytkownika. Aby to obsługiwać wydaje trzeba zachować co najmniej jedno odwołanie do DbContext dla życia wątku UI ..

private static readonly SourceLogContext DbUILazyLoadContext = new SourceLogContext(); 

Chciałbym powitać komentarze dotyczące tych punktów ..

+2

Czy kiedykolwiek rozwiązałeś problem? Mam podobny problem. –

+0

Naprawdę nie zrozumiałem lepiej niż to, co mam w swojej odpowiedzi. Możesz zobaczyć pozycję dodawaną do dwóch kolekcji w metodzie [AddNewLogEntry] (https://github.com/tomhunter-gh/SourceLog/blob/aed3718af18fcff471f04c83f83a0160b97b6829/SourceLog.Model/LogSubscription.cs#L90), raz w kontekście kolekcja i raz do "kolekcji UI". –

+0

Miałem ten sam problem, który okazał się pracownikiem tła próbującym uzyskać dostęp do kontekstu, podczas gdy inne procesy go używały. Przeniosłem to połączenie po zakończeniu innych procesów i to rozwiązało. Sądzę, że działając również w kolejce. O ile mi wiadomo, EF powinien zarządzać wątkami, nigdy nie miałem takiego problemu, dopóki nie utworzyłem połączenia w tle. – Hannish

0

An AccessViolationException występuje tylko wtedy, gdy możliwy do zweryfikowania zarządzany kod współdziała z niezarządzanym kodem lub z niebezpiecznym kodem zarządzanym.

Trzeba przejść przez ten blog moje SM na temat sposobu rozwiązywania dostępu 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

Dzięki, mam przeczytaj ten artykuł. Używam SQL CE 4.0 i jak stwierdza artykuł "aplikacja powinna serializować dostęp do tych obiektów" Użyłem 'blokady' do serializowania wywołań do' SaveChanges() '.. –

+0

(ale nadal dostaję błąd. .) –

Powiązane problemy