C# 如何在DbContext-主键中并行运行Xunit而不发生冲突?

C# 如何在DbContext-主键中并行运行Xunit而不发生冲突?,c#,entity-framework,asp.net-core,.net-core,xunit,C#,Entity Framework,Asp.net Core,.net Core,Xunit,我正在使用内存数据库创建Xunit测试。如果单独运行,测试将正确执行。但是,如果并行运行,它们会由于dbcontext中的主键问题而发生冲突 解决这个问题的最佳选择是什么 Xunit是否具有拆卸功能?听说xunit没有 支持这一点 我应该按顺序运行测试吗 应该继续使用不同的密钥ID吗 试图研究xunit文档,刚刚开始学习.net编程 错误: "System.ArgumentException : An item with the same key has already been added.

我正在使用内存数据库创建Xunit测试。如果单独运行,测试将正确执行。但是,如果并行运行,它们会由于dbcontext中的主键问题而发生冲突

解决这个问题的最佳选择是什么

  • Xunit是否具有拆卸功能?听说xunit没有 支持这一点
  • 我应该按顺序运行测试吗
  • 应该继续使用不同的密钥ID吗
  • 试图研究xunit文档,刚刚开始学习.net编程

    错误:

    "System.ArgumentException : An item with the same key has already been added. Key: 2"
    
    代码:

    "System.ArgumentException : An item with the same key has already been added. Key: 2"
    
    2是主键,使用两次

    public class ProductAppServiceTest
    {
        public TestContext context;
        public IMapper mapper;
        public ProductAppServiceTest()
        {
            var options = new DbContextOptionsBuilder<TestContext>()
                .UseInMemoryDatabase(databaseName: "TestDatabase")
                .Options;
            context = new TestContext(options);
    
            ApplicationServicesMappingProfile applicationServicesMappingProfile = new ApplicationServicesMappingProfile();
            var config = new MapperConfiguration(cfg =>
            {
                cfg.AddProfile(applicationServicesMappingProfile);
            });
            mapper = config.CreateMapper();
        }
    
        [Fact]
        public async Task Get_ProductById_Are_Equal()
        {
            context.Product.Add(new Product { ProductId = 2, ProductCode = "123", ProductName = "ABC" });
            context.SaveChanges();
    
            var ProductRepository = new ProductRepository(context);
            var ProductAppService = new ProductAppService(ProductRepository, mapper);
            var ProductDto = await ProductAppService.GetProductById(2);
    
            Assert.Equal("123", ProductDto.ProductCode);
        }
    
        [Fact]
        public async Task Get_ProductPrice_Are_Equal()
        {
            context.Product.Add(new Product { ProductId = 2, ProductCode = "123", ProductName = "ABC" });
            context.SaveChanges();
    
            var ProductRepository = new ProductRepository(context);
            var ProductAppService = new ProductAppService(ProductRepository, mapper);
            var ProductDto = await ProductAppService.GetProductById(2);
            //Goes into Enum table to validate price is 5
            Assert.Equal("5", ProductDto.Price);
        }
    
    公共类ProductAppServiceTest
    {
    公共测试语境;
    公共图像映射器;
    公共产品AppServiceTest()
    {
    var options=new DbContextOptionsBuilder()
    .UseInMemoryDatabase(数据库名称:“TestDatabase”)
    .选择;
    上下文=新的TestContext(选项);
    ApplicationServicesMappingProfile ApplicationServicesMappingProfile=新的ApplicationServicesMappingProfile();
    var config=new-MapperConfiguration(cfg=>
    {
    AddProfile(applicationServicesMappingProfile);
    });
    mapper=config.CreateMapper();
    }
    [事实]
    公共异步任务Get_ProductById_Are_Equal()
    {
    context.Product.Add(新产品{ProductId=2,ProductCode=“123”,ProductName=“ABC”});
    SaveChanges();
    var ProductRepository=新的ProductRepository(上下文);
    var ProductAppService=新的ProductAppService(ProductRepository,mapper);
    var ProductDto=await ProductAppService.GetProductById(2);
    Assert.Equal(“123”,ProductDto.ProductCode);
    }
    [事实]
    公共异步任务Get_ProductPrice_Are_Equal()
    {
    context.Product.Add(新产品{ProductId=2,ProductCode=“123”,ProductName=“ABC”});
    SaveChanges();
    var ProductRepository=新的ProductRepository(上下文);
    var ProductAppService=新的ProductAppService(ProductRepository,mapper);
    var ProductDto=await ProductAppService.GetProductById(2);
    //进入枚举表以验证价格是否为5
    断言相等(“5”,产品与价格之比);
    }
    
    这里的问题是,每个测试都使用相同的内存数据库。虽然可以为每个测试拆卸和创建数据库,但通常更容易为每个测试使用唯一的数据库

    注意:每个测试使用相同数据库的原因是因为您使用的是静态数据库名称

    XUnit在每次测试之前调用测试类构造函数,因此您可以使用
    guid
    作为数据库名称,为每个测试创建一个唯一的内存中数据库

    var options=new DbContextOptionsBuilder()
    .UseInMemoryDatabase(数据库名称:Guid.NewGuid().ToString())
    .选择;
    上下文=新的TestContext(选项);
    
    示例测试将无法正确并行执行,因为它们是同一集合的成员

    默认情况下,每个测试类都是唯一的测试集合 同一个测试类不会彼此并行运行。 -

    源代码中的TestContext是什么,它是某种类型的单例吗?如果是这样,当并行运行时,因为两个测试都在同一个集合中,它将在xUnit启动的类的两个实例之间共享(即使在同一个集合中创建了每个类的新副本)

    如果您确实需要在集合或程序集中共享,我建议您使用内置的TestFixture功能,或者实现一个程序集夹具

    xUnit中的安装和拆卸是通过构造函数和IDisposable实现的,或者通过在测试类上实现IAsyncLifetime实现的。注意:并行运行时,DisposeSync将在启动每个dispose之前等待测试运行

    public class UnitTest : IDisposable, IAsyncLifetime
    {
        public UnitTest()
        {
            //First
        }
    
        public Task InitializeAsync()
        {
            //Second
        }
    
        [Fact]
        public async Task Test1()
        {
            //Third
        }
    
        public Task DisposeAsync()
        {
            //Forth
        }
    
        public void Dispose()
        {
            //Fifth
        }
    }
    

    为了避免多处理任务更改其他任务使用的数据时出现问题,我的简单答案是开发一个数据结构,其中每个任务都传递一个索引,告诉它们要使用结构的哪一部分,当并行作业全部完成时,数据就可以重新组合到原始的主数据结构中您通常使用的。

    为每个测试拆卸或创建新的上下文。您是否尝试过设置
    数据库名称:$“{Guid.NewGuid()}”
    ,而不是设置
    数据库名称:“TestDatabase”
    ,以便每个并行运行将使用不同的数据库进行测试?使用相同的内存中数据库可能是它们相互重叠的原因。