为什么 .NET Core 作用域会影响数据库上下文更新的可见性?
Why does .NET Core scope affect how db context updates are visible?
我在单元测试中发现了 .NET dbContext 和范围界定的一些有趣行为,但我一直无法弄清楚为什么会这样。希望有人知道这是为什么。
这是来自测试方法。这是步骤。为了清楚起见,我试图将其编辑下来。
- 测试 class 安装程序添加依赖注入所需的服务,并将数据库创建为 InMemory 数据库,并为测试数据播种。
... add services ...
services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("ApplicationDb"));
_serviceProvider = services.BuildServiceProvider();
_serviceScopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
_dbContext = _serviceProvider.GetRequiredService<MyDbContext>();
... seed data ...
- 测试方法修改一些数据,然后调用被测试的目标方法。
//signature: public async Task TargetMethod(SomeTaskDto taskDto, IServiceScopeFactory serviceScopeFactory)
... modify some data using _dbContext created in Setup ...
var _myClass =_serviceProvider.GetRequiredService<MyClass>();
await _myClass.TargetMethod( taskDto, _serviceScopeFactory );
- 目标方法然后使用
_serviceScopeFactory
获取数据库上下文并修改一些数据。
using (var scope = serviceScopeFactory.CreateScope())
{
var _db = scope.ServiceProvider.GetService<MyDbContext>();
... changes made by calling test method are visible here.
... modify some data ...
await _db.SaveChangesAsync();
... other stuff ...
} //end scope
- 回到测试方法,调用目标方法后,原始数据库上下文中看不到更改:
var entityShouldBeModified = _dbContext.Products.Where( x => x.Id = idOfModifiedEntity ).FirstOrDefault();
//this fails:
Assert.AreEqual( expectedUpdatedValue, entityShouldBeModified.PropertyWhichShouldBeModified );
- 如果我修改测试方法以在调用目标方法后创建一个新的作用域,并获得一个新的数据库上下文,则目标方法的更新数据现在可见:
using(var scope = _serviceScopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetService<MyDbContext>();
db.SetAuthenticatedUser(_user);
var entityShouldBeModified = db.Products.Where( x => x.Id = idOfModifiedEntity ).FirstOrDefault();
//this works:
Assert.AreEqual( expectedUpdatedValue, entityShouldBeModified.PropertyWhichShouldBeModified );
}
这是否只是单元测试中 InMemory 数据库发生的事情? (我想我真的应该在 Sql 服务器数据库上尝试一下。)
即使它只在 InMemory dbs 上,为什么测试方法所做的更改对目标方法可见,而目标方法所做的更改对调用测试方法不可见?
为什么我必须创建一个新的范围和新的数据库上下文才能看到目标方法所做的更改?
谢谢,感谢任何解释。
我认为是因为DbContext 是一个缓存。
您不会读取最新数据,您的修改仍将应用于数据库。
如下所示的实验。
var record = dbContext1.Records.First();
using (var scope = services.CreateScope())
{
var dbContext2 = scope.ServiceProvider.GetRequiredService<TestDbContext>();
Debug.WriteLine($"Recourds.Count in dbContext1 = {dbContext1.Records.Count()}");
Debug.WriteLine($"Recourds.Count in dbContext2 = {dbContext2.Records.Count()}");
Debug.WriteLine($"1st Record.Name in dbContext1 = {dbContext1.Records.Single(o => o.Id == 1).Name}");
Debug.WriteLine($"1st Record.Name in dbContext2 = {dbContext2.Records.Single(o => o.Id == 1).Name}"); //if you comment this line, the name won't be cached
dbContext2.Records.Add(new DailyRecord()
{
Name = "Sansas",
Date = DateTime.Today,
}); // add a record
dbContext2.SaveChanges();
record.Name = "Candy"; //change name
dbContext1.SaveChanges();
Debug.WriteLine($"Recourds.Count in dbContext1 = {dbContext1.Records.Count()}");
Debug.WriteLine($"Recourds.Count in dbContext2 = {dbContext2.Records.Count()}");
Debug.WriteLine($"1st Record.Name in dbContext1 = {dbContext1.Records.Single(o => o.Id == 1).Name}");
Debug.WriteLine($"1st Record.Name in dbContext2 = {dbContext2.Records.Single(o => o.Id == 1).Name}");
"Records.Count in dbContext1 = 1"
"Records.Count in dbContext2 = 1"
"Record.Name in dbContext1 = Sansas"
"Record.Name in dbContext2 = Sansas"
"Records.Count in dbContext1 = 2"
"Records.Count in dbContext2 = 2"
"Record.Name in dbContext1 = Candy"
"Record.Name in dbContext2 = Sansas"
dbContext2 读取缓存名称。
我在单元测试中发现了 .NET dbContext 和范围界定的一些有趣行为,但我一直无法弄清楚为什么会这样。希望有人知道这是为什么。
这是来自测试方法。这是步骤。为了清楚起见,我试图将其编辑下来。
- 测试 class 安装程序添加依赖注入所需的服务,并将数据库创建为 InMemory 数据库,并为测试数据播种。
... add services ...
services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("ApplicationDb"));
_serviceProvider = services.BuildServiceProvider();
_serviceScopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
_dbContext = _serviceProvider.GetRequiredService<MyDbContext>();
... seed data ...
- 测试方法修改一些数据,然后调用被测试的目标方法。
//signature: public async Task TargetMethod(SomeTaskDto taskDto, IServiceScopeFactory serviceScopeFactory)
... modify some data using _dbContext created in Setup ...
var _myClass =_serviceProvider.GetRequiredService<MyClass>();
await _myClass.TargetMethod( taskDto, _serviceScopeFactory );
- 目标方法然后使用
_serviceScopeFactory
获取数据库上下文并修改一些数据。
using (var scope = serviceScopeFactory.CreateScope())
{
var _db = scope.ServiceProvider.GetService<MyDbContext>();
... changes made by calling test method are visible here.
... modify some data ...
await _db.SaveChangesAsync();
... other stuff ...
} //end scope
- 回到测试方法,调用目标方法后,原始数据库上下文中看不到更改:
var entityShouldBeModified = _dbContext.Products.Where( x => x.Id = idOfModifiedEntity ).FirstOrDefault();
//this fails:
Assert.AreEqual( expectedUpdatedValue, entityShouldBeModified.PropertyWhichShouldBeModified );
- 如果我修改测试方法以在调用目标方法后创建一个新的作用域,并获得一个新的数据库上下文,则目标方法的更新数据现在可见:
using(var scope = _serviceScopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetService<MyDbContext>();
db.SetAuthenticatedUser(_user);
var entityShouldBeModified = db.Products.Where( x => x.Id = idOfModifiedEntity ).FirstOrDefault();
//this works:
Assert.AreEqual( expectedUpdatedValue, entityShouldBeModified.PropertyWhichShouldBeModified );
}
这是否只是单元测试中 InMemory 数据库发生的事情? (我想我真的应该在 Sql 服务器数据库上尝试一下。) 即使它只在 InMemory dbs 上,为什么测试方法所做的更改对目标方法可见,而目标方法所做的更改对调用测试方法不可见?
为什么我必须创建一个新的范围和新的数据库上下文才能看到目标方法所做的更改?
谢谢,感谢任何解释。
我认为是因为DbContext 是一个缓存。
您不会读取最新数据,您的修改仍将应用于数据库。
如下所示的实验。
var record = dbContext1.Records.First();
using (var scope = services.CreateScope())
{
var dbContext2 = scope.ServiceProvider.GetRequiredService<TestDbContext>();
Debug.WriteLine($"Recourds.Count in dbContext1 = {dbContext1.Records.Count()}");
Debug.WriteLine($"Recourds.Count in dbContext2 = {dbContext2.Records.Count()}");
Debug.WriteLine($"1st Record.Name in dbContext1 = {dbContext1.Records.Single(o => o.Id == 1).Name}");
Debug.WriteLine($"1st Record.Name in dbContext2 = {dbContext2.Records.Single(o => o.Id == 1).Name}"); //if you comment this line, the name won't be cached
dbContext2.Records.Add(new DailyRecord()
{
Name = "Sansas",
Date = DateTime.Today,
}); // add a record
dbContext2.SaveChanges();
record.Name = "Candy"; //change name
dbContext1.SaveChanges();
Debug.WriteLine($"Recourds.Count in dbContext1 = {dbContext1.Records.Count()}");
Debug.WriteLine($"Recourds.Count in dbContext2 = {dbContext2.Records.Count()}");
Debug.WriteLine($"1st Record.Name in dbContext1 = {dbContext1.Records.Single(o => o.Id == 1).Name}");
Debug.WriteLine($"1st Record.Name in dbContext2 = {dbContext2.Records.Single(o => o.Id == 1).Name}");
"Records.Count in dbContext1 = 1"
"Records.Count in dbContext2 = 1"
"Record.Name in dbContext1 = Sansas"
"Record.Name in dbContext2 = Sansas"
"Records.Count in dbContext1 = 2"
"Records.Count in dbContext2 = 2"
"Record.Name in dbContext1 = Candy"
"Record.Name in dbContext2 = Sansas"
dbContext2 读取缓存名称。