1

I'm building a .NET Core 3 console app with EF Core 2.2.x and EntityFrameworkCore.Jet to perform some generic reading and writing from/to an access database (mdb). I'm compiling for x86 as the docs say. Goal is to migrate lost and lots of data from a legacy system to a newer system (syncing data).

I've scaffolded the database, and I can perfectly read from the database, but as soon as I go and update an entity, I'm getting access violation exceptions:

Code sample (shortened for abbrevity):

public void SetSynced(int id)
{
    var item = _db.Products.Where(x => x.Id == id).Single();
    item.NeedsSyncing = false;
    _db.SaveChanges();
}

When this code is hit, the following Exception shows up (no innerexception)

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I don't understand why this is happening as I can perfectly read the database, it does this only on writing.

I've tried using the following connection strings (drivers) but both with the same end-result:

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\database.mdb;Jet OLEDB:Database Password = password;`
Provider=Microsoft.jet.oledb.4.0;Data Source=C:\\database.mdb;Jet OLEDB:Database Password = password;

When running on x64 reading works, but I'm getting the following exception:

System.Data.OleDb.OleDbException (0x80004002): No error message available, result code: E_NOINTERFACE(0x80004002).

I am lost and searching in the dark here. Does anyone have experience / ideas for me on where to look?

Update 02/02/2020: I've tried manually connecting using OleDb and running the query to see where that brings me. The below code works. When I switch it out with the above code it fails again.

db = new OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\\database.mdb;Jet OLEDB:Database Password = password");
db.Open();
var command = new OleDbCommand($"UPDATE Products SET NeedsSyncing = 0 WHERE Id= 1", db);
var res = command.ExecuteNonQuery();

After setting up JetConfiguration.ShowSqlStatements = true I got the following stacktrace:

ExecuteDbDataReader==========
UPDATE [Products] SET [PictureName] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;


@p1(Int32) = 1
@p0(String) = 'notfound.jpg'
Fatal error. 0xC0000005
   at System.Data.Common.UnsafeNativeMethods+ICommandWithParameters.SetParameterInfo(IntPtr, IntPtr[], System.Data.OleDb.tagDBPARAMBINDINFO[])
   at System.Data.OleDb.OleDbCommand.ApplyParameterBindings(ICommandWithParameters, System.Data.OleDb.tagDBPARAMBINDINFO[])
   at System.Data.OleDb.OleDbCommand.CreateAccessor()
   at System.Data.OleDb.OleDbCommand.InitializeCommand(System.Data.CommandBehavior, Boolean)
   at System.Data.OleDb.OleDbCommand.ExecuteCommand(System.Data.CommandBehavior, System.Object ByRef)
   at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(System.Data.CommandBehavior, System.String)
   at System.Data.OleDb.OleDbCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.OleDb.OleDbCommand.ExecuteDbDataReader(System.Data.CommandBehavior)
   at System.Data.Common.DbCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.Jet.JetCommand.InternalExecuteDbDataReader(System.String, System.Data.CommandBehavior)
   at System.Data.Jet.JetCommand.ExecuteDbDataReader(System.Data.CommandBehavior)
   at System.Data.Common.DbCommand.ExecuteReader()
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(Microsoft.EntityFrameworkCore.Storage.IRelationalConnection, Microsoft.EntityFrameworkCore.Diagnostics.DbCommandMethod, System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>)
   at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(Microsoft.EntityFrameworkCore.Storage.IRelationalConnection, System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.Object>)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(Microsoft.EntityFrameworkCore.Storage.IRelationalConnection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(Microsoft.EntityFrameworkCore.DbContext, System.ValueTuple`2<System.Collections.Generic.IEnumerable`1<Microsoft.EntityFrameworkCore.Update.ModificationCommandBatch>,Microsoft.EntityFrameworkCore.Storage.IRelationalConnection>)
   at Microsoft.EntityFrameworkCore.Storage.Internal.NoopExecutionStrategy.Execute[[System.ValueTuple`2[[System.__Canon, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.__Canon, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]], System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]](System.ValueTuple`2<System.__Canon,System.__Canon>, System.Func`3<Microsoft.EntityFrameworkCore.DbContext,System.ValueTuple`2<System.__Canon,System.__Canon>,Int32>, System.Func`3<Microsoft.EntityFrameworkCore.DbContext,System.ValueTuple`2<System.__Canon,System.__Canon>,Microsoft.EntityFrameworkCore.Storage.ExecutionResult`1<Int32>>)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(System.Collections.Generic.IEnumerable`1<Microsoft.EntityFrameworkCore.Update.ModificationCommandBatch>, Microsoft.EntityFrameworkCore.Storage.IRelationalConnection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(System.Collections.Generic.IReadOnlyList`1<Microsoft.EntityFrameworkCore.Update.IUpdateEntry>)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(System.Collections.Generic.IReadOnlyList`1<Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry>)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at Cashmaster.BackgroundWorker.Services.CashMasterService.Crash()

Nuget packages + versions (related to Jet):

Microsoft.EntityFrameworkCore 2.2.6
EntityFrameworkCore.Jet 2.2.0
System.Data.OleDb 4.7.0

Running this all on .NET Core 3.1.

Erik J.
  • 799
  • 6
  • 19
  • 1
    About Access violation exception it is a jet oledb provider bug. From time to time happens. You can try compacting database. You can also try to use a different version of the jet oledb provider or use a x64 version. Sometimes is related to multithreading. Try also to disable or enable connection pooling in system.data.jet – bubi Feb 01 '20 at 08:58
  • @bubi I've tried with `OLE DB Services=1` in the connection string as well as `-1` and `0`, but there's no change. I'm using a lot of async methods though, but moving everything to a sync-way-of-working is not solving my problem. I have enabled and disabled connection pooling (when adding the dbcontext to the di container using the `JetConfiguration.UseConnectionPooling` param) but this has no change. Is there a simple way to install the v14 of the jetdriver on both x86 and x64? – Erik J. Feb 01 '20 at 11:06
  • Extra info: I've also installed the access 2016 data tools (therefor upgrading to `Microsoft.ACE.OLEDB.16.0` but this throws me the same exception on x86 (x64 doesn't work there for reading). – Erik J. Feb 01 '20 at 11:14
  • 1
    @bubi I've validated that a regular old oledbconnection does the trick, so it might indeed have something to do with multithreading. I'm using the 'simple' DI that MFT provides for e.g. ASP.NET. How can I debug this issue further? – Erik J. Feb 02 '20 at 15:51
  • do you mean the old OLEDB provider or using an old .Net Framework and so working with an old OleDbConnection? – bubi Feb 04 '20 at 07:31
  • With "old" I mean a simple OleDbConnection (same application in .NET Core 3.1). So basically running OleDbConnection myself for updates works fine (same connection string). – Erik J. Feb 04 '20 at 17:27
  • I think that it could be something related to Migration. You can try to enable debugging of the queries so you can see wich query the EF provider runs against the System.Data.Jet provider. To do so you can set `JetConfiguration.ShowSqlStatements = true` – bubi Feb 05 '20 at 11:37
  • @bubi I did so, added the stacktrace it to the initial post. No migrations (I scaffolded the database). The query seems very simple. My code gets the first entry from a table, changes a param, and saves it (synchronously, to make it simple). I've also added the nuget package versions just to make sure. – Erik J. Feb 05 '20 at 21:43
  • Sorry I lost it. Could your run the query `UPDATE [Products] SET [PictureName] = @p0 WHERE [Id] = @p1` using parameters `@p1(Int32) = 1` and `@p0(String) = 'notfound.jpg'`? Could you try both `ExecuteNonQuery` and `ExecuteDbDataReader`? – bubi Feb 06 '20 at 13:09
  • @Bubi I've just done a test, as soon as I'm using parameters both the ExecuteNonQuery as well as the ExecuteDbDataReader crash with the accessviolation error I started my post with. When using plain queries (with values in the actual query) it works. – Erik J. Feb 09 '20 at 19:34
  • It's a jet oledb provider bug. I don't know any workaround. If you have a contract with Microsoft you could try to contact them... – bubi Feb 09 '20 at 20:12
  • Right, I see. I don't have any contacts (at least not in this area of expertise) so I'm afraid this is it. I'll just be doing manual queries (and escaping) for queries that alter data then. Thanks for the help! – Erik J. Feb 10 '20 at 09:06
  • You could also try to ask on stack overflow with another tag related to access, c# and oledb... – bubi Feb 11 '20 at 13:55

1 Answers1

0

The issue was tracked on GitHub by Migrate the x86 specific structures to AnyCPU for System.Data.OleDb #32509 and fixed with Fix x86 packing issues in System.Data.OleDb #33899 by a community member.

To fix the issue in your project, use the latest version of EF Core 3.1.x (currently 3.1.10) with the latest version of EntityFrameworkCore.Jet (currently prerelease 3.1.0-alpha.4) together with System.Data.OleDb release 5.0.0.

lauxjpn
  • 4,749
  • 1
  • 20
  • 40