So I'm working on testing parts of my code adding and removing data in my database. I'm setting up an In memory database for each unit test to make sure that I got a completely "clean slate" for each test. However, I have encountered a very weird issue. I have omitted some code below but it shows my general approach:
[TestFixture]
internal class EventControllerTest
{
[Test]
public void CreateEventController()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "CreateEventController1")
.Options;
var context = new ApplicationDbContext(options, null);
//Add eventType.
realEventService = new EventService(context, currentUserService.Object);
realEventService.CreateEventType(new EventTypeData
{
Color = "Pink"
});
//ASSERTS
}
[Test]
public void GetEventController()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "GetEventController1")
.Options;
var context = new ApplicationDbContext(options, null);
//Add eventType.
realEventService = new EventService(context, currentUserService.Object);
realEventService.CreateEventType(new EventTypeData
{
Color = "Pink",
});
//ASSERTS
}
}
Now, If I run each of these tests by themselves, they pass as I originally check the Id of the EventType, they are both 1. However, if I run all my test in sequence, my second test fails. It fails because the EventType that it adds actually has the id of 2 and not 1! However, the database only contains one entry of an EventType. I have separate names on the databases and to my understanding, this means that they are entirely different. And yet, the auto-incremented index is for some reason just increased and used in my second test. So to clarify:
How can I separate these databases entirely?
Looks like an open issue / bug: https://github.com/aspnet/EntityFrameworkCore/issues/6872
They do offer an extension method as a fix (code lifted as is from there):
public static class DbContextExtensions
{
public static void ResetValueGenerators(this DbContext context)
{
var cache = context.GetService<IValueGeneratorCache>();
foreach (var keyProperty in context.Model.GetEntityTypes()
.Select(e => e.FindPrimaryKey().Properties[0])
.Where(p => p.ClrType == typeof(int)
&& p.ValueGenerated == ValueGenerated.OnAdd))
{
var generator = (ResettableValueGenerator)cache.GetOrAdd(
keyProperty,
keyProperty.DeclaringEntityType,
(p, e) => new ResettableValueGenerator());
generator.Reset();
}
}
}
public class ResettableValueGenerator : ValueGenerator<int>
{
private int _current;
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
=> Interlocked.Increment(ref _current);
public void Reset() => _current = 0;
}
To use, call context.ResetValueGenerators(); before the context is used for the first time and any time that EnsureDeleted is called. For example:
using (var context = new BlogContext())
{
context.ResetValueGenerators();
context.Database.EnsureDeleted();
context.Posts.Add(new Post {Title = "Open source FTW", Blog = new Blog {Title = "One Unicorn"}});
context.SaveChanges();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.