实体框架核心:在InMemoryDb测试中启用重复的主键 [英] Entity Framework Core: Enable Duplicate Primary Keys in InMemoryDb Testing

查看:56
本文介绍了实体框架核心:在InMemoryDb测试中启用重复的主键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在下面的Entity Framework Core 3.1 InMemoryDB单元测试中收到此错误.

I am receiving this error below in Entity Framework Core 3.1, InMemoryDB Unit Test.

无法跟踪实体类型的实例,因为另一个实例键值已被跟踪.附加现有时实体,请确保只有一个具有给定键值的实体实例附件.

The instance of entity type cannot be tracked because another instance with the key value is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

堆栈跟踪:IdentityMap 1.ThrowIdentityConflict(InternalEntityEntry条目)IdentityMap 1.Add(TKey键,InternalEntityEntry条目,布尔值updateDuplicate)IdentityMap`1.Add(TKey键,InternalEntityEntry条目)

Stack Trace: IdentityMap1.ThrowIdentityConflict(InternalEntityEntry entry) IdentityMap1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate) IdentityMap`1.Add(TKey key, InternalEntityEntry entry)

基本上,我要添加一个具有相同PrimaryKey ID的值.我在所有查询中都有 AsNoTracking 以及 QueryTrackingBehavior.NoTracking ,并且仍然出现错误.

Basically, I am adding a value with the same PrimaryKey Id. I have AsNoTracking in all queries along with QueryTrackingBehavior.NoTracking, and its still giving error.

如何在InMemory数据库单元测试中启用重复的主键?我还有什么需要做的?

How do I Enable duplicate Primary Keys in InMemory DB Unit testing? Anything else I should do?

services.AddDbContext<PropertyContext>(
   a => a.UseInMemoryDatabase($"Ipts-{Guid.NewGuid()}")
   .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking).EnableSensitiveDataLogging(),
    ServiceLifetime.Scoped);

new Property
{
    PropertyId = 1,
    

其背后的历史,当前正在将现有的单元测试数据集迁移到EFInMemoryDb中.

History behind this, Currently migrating a existing unit test dataset, into EFInMemoryDb.

推荐答案

如果要在EF DbSet中插入带有重复键的行,则不能,这是不可能的.键必须是唯一的.但是,如果由于测试记录具有相同的ID(但反映特定于该测试的数据)而试图将每个测试的数据插入到DbContext中,则将获得IdentityConflict异常,这是因为您的DbContext数据源位于测试之间

If you are wanting to insert rows with a duplicate key into an EF DbSet, then no, this isn't possible. Keys must be unique. However, if you are trying to insert data for each test into a DbContext are getting the IdentityConflict exception since the test records have the same ID (but reflect data specific to that test) then it is because your DbContext data source is living between the tests.

通常,在诸如集成测试之类的测试方案中,该过程是测试设置或至多测试夹具设置会将DbContext实例化到新的内存中,并初始化该测试或测试套件的数据.当测试/夹具拆除时,将放置DbContext和内存中的数据存储.请注意,这通常是为集成测试而不是单元测试完成的,因为内存数据库比SQL Express之类的数据库要快得多,但这仍然意味着在每个测试或一组测试之间建立和拆除数据状态.单元测试被设计为在开发期间连续不断地临时运行(以帮助您在准备提交更改之前标记中断的更改),而集成测试则可以在提交之前或作为提交的一部分运行过程.

Normally with testing scenarios such as integration testing the process is that a test setup or at most a test fixture setup would instantiate the DbContext to a new in-memory store and initialize the data for that test, or suite of tests. When the test/fixture tears down, the DbContext and in-memory data store is disposed. Note that this is typically done for integration tests, not unit tests specifically because while an in-memory database is a lot faster than something like SQL Express, it still means setting up and tearing down data state between each test or set of tests. Unit tests are designed to be run continuously ad-hoc while you are developing (to help flag breaking changes before you're ready to commit those changes) while integration tests would be run either just prior to a commit, or as part of the commit process.

按照我之前的回答(实体Framework Core 3:基于DBContext的接口是一种很好的做法?)单元测试的一个很好的选择是存储库模式,它可以避免模拟整个DbContext或需要设置和拆除已知数据状态.存储库的单元测试模型被配置为返回您要围绕其测试业务逻辑的特定已知状态.集成测试当然仍然很重要,但是它们旨在始终通过EF DbContext& amp; 声明数据状态行为.存储库.

As per my earlier answer (Entity Framework Core 3: Interface on DBContext good practice?) a good alternative for unit tests is the Repository pattern to avoid mocking an entire DbContext or requiring a setup and teardown of known data state. The unit test mock of the repository is configured to return the specific known state you want to test business logic around. Integration tests certainly are still important, but they are geared towards asserting data state behaviour all the way through the EF DbContext & Repository.

例如,如果您有一个要处理一组订单的单元测试,并且有一个带有GetCustomerOrders方法的OrderRepository,该方法返回了一个 IQueryable< Order> ,则该仓库的模型可以看起来像这样:(最小起订量示例)

So as an example if you have a Unit Test that wants to work on a set of Orders and had an OrderRepository with a GetCustomerOrders method that returned an IQueryable<Order> then the mock of the repository can look something like this: (Moq example)

// Setting up the test data could be done in the test or a separate method.
var orders = new []
{
    new Order { OrderId = 1, /* populate expected order details, including references */ },
    new Order { OrderId = 2, }
}.ToList();

var mockOrderRepository = new Mock<IOrderRepository>();
mockOrderRepository.Setup(x => x.GetCustomerOrders(customerId))
    .Returns(orders.AsQueryable());

IOrderService serviceUnderTest = new OrderService(mockOrderRepository.Object); // plus other mocked dependencies...

var result = serviceUnderTest.DoSomethingForCustomer(customerId);
mockOrderRepository.Verify(x => x.GetCustomerOrders(customerId), Times.Once);
// other asserts and result check...

使用内存数据库进行此操作需要将内存数据库的范围限定在测试范围内,或者确保测试引用的数据与任何其他测试引用的数据不冲突.

Doing this with an in-memory database would require the in-memory database to be scoped to the test, or ensure that data referenced by the test doesn't conflict with the data referenced by any other test.

这篇关于实体框架核心:在InMemoryDb测试中启用重复的主键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆