Jimmyt1988
Jimmyt1988

Reputation: 21136

The model backing the 'EFDbContextProxy' context has changed - Testing cache?

I made a change to an entity, ran a migration and updated the database. I then re-ran a test and it's throwing an error:

The model backing the 'EFDbContextProxy' context has changed since the database was created. Consider using Code First Migrations to update the database

If I run the application, I do not get this error. It seems my EFDbContextProxy (Whatever the hell that is) is using an old cache of the data structure.

Any ideas how to resolve this?

This is how I prepare my db context in my test:

public class EFFieldViewerRepository_Setup
{
    protected DateTime now;
    protected Mock<EFDbContext> mockDbContext;
    protected EFFieldViewerRepository fieldViewerRepository;
    protected IDbSet<FieldViewer> fieldViewerDbSet;

    public EFFieldViewerRepository_Setup()
    {
        this.now = DateTime.Now;
        this.mockDbContext = new Mock<EFDbContext>() { CallBase = true };
        this.fieldViewerRepository = new EFFieldViewerRepository( this.mockDbContext.Object );
        ...
    }

Here is my stack trace: http://pastebin.com/kAUmhi3j

Upvotes: 1

Views: 393

Answers (2)

Mike Atkisson
Mike Atkisson

Reputation: 601

LocalDB Database Might be the Culprit (sounds strange, I know)

If Entity Framework is configured to produce a LocalDB database, Moq's DBContext proxy class created by Castle will, in turn, create a LocalDB database which may be out of sync with EF Migrations added after the database is initially created. I found that deleting the LocalDB database would solve the problem, though there are probably better solutions including the answer provided @Jimmyt1988.

Entity Framework Configuration

I was running unit tests with Moq in a Visual Studio test project which also had integration tests that connected to the target database. Because of this, I had ended up having a node in my app.config like this.

<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
  <parameters>
    <parameter value="mssqllocaldb" />
  </parameters>
</defaultConnectionFactory>
<providers>
  <provider invariantName="Oracle.ManagedDataAccess.Client" type="Oracle.ManagedDataAccess.EntityFramework.EFOracleProviderServices, Oracle.ManagedDataAccess.EntityFramework, Version=6.122.18.3, Culture=neutral, PublicKeyToken=89b483f429c47342" />
  <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>

Moq Test Setup

My Moq test setup is similar to the @Jimmyt1988's

private Mock<DbSet<OutgoingEntry>> _set;
private Mock<MyContext> _context;
private ModelService<OutgoingEntry, MyContext> _service;

[NUnit.Framework.SetUp]
public void Setup()
{
    _set = new Mock<DbSet<OutgoingEntry>>();
    _set.Reset();
    _context = new Mock<MyContext>();
    _context.Reset();
    _context.Setup(m => m.OutgoingEntries).Returns(_set.Object);
    _context.Setup(m => m.Set<OutgoingEntry>()).Returns(_set.Object);
    _service = new OutgoingService(_context.Object);
}

The Mysterious Proxy Class and LocalDB

In the example above, _context.Object is an instance of Castle.Proxies.MyContextProxy. This is generated at runtime to allow Moq to override Methods and Properties. However, if EF is configured as above, generation of the proxy class will still trigger database initialization. This ultimately results in a database like this:

enter image description here

Breaking Things without Intent, and then Fixing them in a Clumsy Way

All the details above were more or less transparent to me when I added a new migration. This, unfortunately, resulted in the exact error mentioned in the original question. To resolve the underlying issue, I simply deleted the LocalDB file created by Moq/Castle. It's trickier than it sounds. I found these specific steps worked on Windows 10.

  • Open Powershell as an Administrator.
  • Invoke SqlLocalDB.exe stop mssqllocaldb.
  • Now Invoke SqlLocalDB.exe delete mssqllocaldb This command will effectively delete any LocalDB databases you have. Only do this if you don't care about your LocalDB mssqllocaldb instance.
  • Delete the mdf and log files created by Moq / Castle. I found these in my Windows home directory.
  • Re-run the tests in Visual Studio's test runner.

Upvotes: 1

Jimmyt1988
Jimmyt1988

Reputation: 21136

I had to create a MockEFDbContext and extend off of EFDbContext. Inside the construct I run the Database.SetInitializer<EFDbContext>(null):

public class MockEFDbContext : EFDbContext
{
    public MockEFDbContext()
    {
        Database.SetInitializer<EFDbContext>(null);
    }
}

I then used this new MockEFDbContext in my tests rather than the normal one:

this.mockDbContext = new Mock<MockEFDbContext>() { CallBase = true };

Sources:
- http://gaui.is/how-to-mock-the-datacontext-entity-framework/
- https://msdn.microsoft.com/en-us/library/gg679461(v=vs.113).aspx

Upvotes: 1

Related Questions