Reputation: 5471
I hit a problem when using the in memory database for unit testing with EF 7. The following application will demonstrate the problem.
using System;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
namespace EF7InMemoryBug
{
public class Program
{
public static void Main(string[] args)
{
Program p = new Program();
MembershipContext m1 = p.GetNewContext();
MembershipContext m2 = p.GetNewContext();
foreach (var member in m1.Members)
{
Console.WriteLine(member);
}
}
private MembershipContext GetNewContext()
{
var optionsBuilder =
new DbContextOptionsBuilder<MembershipContext>();
optionsBuilder.UseInMemoryDatabase();
MembershipContext context = new MembershipContext(optionsBuilder.Options);
Member member1 = new Member()
{
MemberId = 1,
FirstName = "James",
LastName = "Jones"
};
context.Members.Add(member1);
context.SaveChanges();
return context;
}
}
public class Member
{
public int MemberId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return $"{MemberId} {FirstName} {LastName}";
}
}
public class MembershipContext : DbContext
{
public MembershipContext(DbContextOptions options)
: base(options) {}
public DbSet<Member> Members { get; set; }
}
}
After calling MembershipContext m2 = p.GetNewContext();
an exception is thrown on the context.SaveChanges()
.
An exception of type 'System.ArgumentException' occurred in EntityFramework.Core.dll but was not handled in user code
Additional information: An element with the same key but a different value already exists. Key: Microsoft.Data.Entity.ChangeTracking.Internal.SimpleKeyValue`1[System.Int32]
I know what is happening, but I don't know why.
When MembershipContext context = new MembershipContext(optionsBuilder.Options);
is called the second time, the context is created, BUT the context.Members
already has the entry from the first time I created the context, hence the exception.
Upvotes: 3
Views: 1291
Reputation: 5471
It's not a bug, it's a feature. https://github.com/aspnet/EntityFramework.Docs/issues/95
Here is the suggested way using DI.
using System;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
namespace EF7InMemoryBug
{
public class Program
{
public static void Main(string[] args)
{
Program p = new Program();
MembershipContext m1 = p.GetNewContext();
MembershipContext m2 = p.GetNewContext();
foreach (var member in m1.Members)
{
Console.WriteLine(member);
}
foreach (var member in m2.Members)
{
Console.WriteLine(member);
}
}
private MembershipContext GetNewContext()
{
var serviceCollection = new ServiceCollection();
serviceCollection
.AddEntityFramework()
.AddInMemoryDatabase()
.AddDbContext<MembershipContext>(c => c.UseInMemoryDatabase());
MembershipContext context = serviceCollection.BuildServiceProvider().GetService<MembershipContext>();
Member member1 = new Member()
{
MemberId = 1,
FirstName = "James",
LastName = "Jones"
};
context.Members.Add(member1);
context.SaveChanges();
return context;
}
}
public class Member
{
public int MemberId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return $"{MemberId} {FirstName} {LastName}";
}
}
public class MembershipContext : DbContext
{
public MembershipContext(DbContextOptions options)
: base(options) {}
public DbSet<Member> Members { get; set; }
}
}
Upvotes: 1