Entity Framework Core:在 InMemoryDb 测试中启用重复主键 [英] Entity Framework Core: Enable Duplicate Primary Keys in InMemoryDb Testing

查看:33
本文介绍了Entity Framework Core:在 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.

堆栈跟踪:IdentityMap1.ThrowIdentityConflict(InternalEntityEntry entry) IdentityMap1.Add(TKey key, InternalEntityEntry entry, Boolean updateDuplicate)IdentityMap`1.Add(TKey key, InternalEntityEntry entry)

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

基本上,我添加了一个具有相同 PrimaryKey Id 的值.我在所有查询中都有 AsNoTrackingQueryTrackingBehavior.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 DB 单元测试中启用重复的主键?还有什么我应该做的吗?

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 中,那么不,这是不可能的.键必须是唯一的.但是,如果您尝试将每个测试的数据插入 DbContext 会收到 IdentityConflict 异常,因为测试记录具有相同的 ID(但反映特定于该测试的数据),那是因为您的 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.

根据我之前的回答(实体框架核心 3:关于 D​​BContext 良好实践的接口?)单元测试的一个很好的替代方案是存储库模式,以避免模拟整个 DbContext 或需要设置和拆除已知数据状态.存储库的单元测试模拟配置为返回您想要测试业务逻辑的特定已知状态.集成测试当然仍然很重要,但它们旨在通过 EF DbContext 和 一直断言数据状态行为.存储库.

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 然后是存储库的模拟看起来像这样:(Moq 示例)

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.

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

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