C#内存数据库共享索引吗?

时间:2018-07-27 17:11:31

标签: c# database unit-testing

因此,我正在测试部分代码,以在数据库中添加和删除数据。我正在为每个单元测试设置一个内存数据库,以确保每个测试都获得完全“干净的状态”。但是,我遇到了一个非常奇怪的问题。我在下面省略了一些代码,但是它显示了我的一般方法:

[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
    }
}

现在,如果我自己运行这些测试中的每个测试,则它们在我最初检查EventType的ID时均通过,它们均为1。但是,如果我依次运行所有测试,则第二个测试将失败。它失败是因为添加的EventType实际上具有2而不是1的ID!但是,数据库仅包含一个EventType条目。我在数据库上有不同的名称,据我所知,这意味着它们是完全不同的。但是,由于某种原因,自动增加的索引刚刚增加并在我的第二次测试中使用。所以要澄清一下:

  • 如果我运行CreateEventController(),则其EventType条目的ID为 一。
  • 如果我运行GetEventController(),则其EventType条目的ID为
    一。
  • 如果我先运行CreateEventController()然后运行GetEventController(),
    CreateEventController()中的EventType条目为1并且EventType GetEventController()中的条目ID为2!

如何完全分离这些数据库?

1 个答案:

答案 0 :(得分:2)

看起来像一个未解决的问题/错误:https://github.com/aspnet/EntityFrameworkCore/issues/6872

他们确实提供了一种扩展方法作为解决方案(从此处直接提升了代码):

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;
}
  

要使用,请调用context.ResetValueGenerators();。在上下文之前   第一次使用,以及在调用确保删除的任何时间使用。对于   例如:

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();
}