我正在尝试将多个实体异步添加到我的数据库中,作为集成测试的“排列”部分。
但是,我的原始代码出现了双重条目:
private async Task<Foo[]> GenerateFoos(params string[] names)
{
var tasks = names.Select(async name =>
{
var foo = new Foo() { Name = name };
await AddAsync(foo);
return foo;
});
await Task.WhenAll(tasks);
return tasks.Select(task => task.Result).ToArray();
}
[Test]
public async Task MyTest()
{
var expected = await GenerateFoos("A", "B", "C");
}
Run Code Online (Sandbox Code Playgroud)
它在我的数据库中生成两倍数量的对象(每个对象两个),我不知道为什么。
我在网上查找了其他示例,但它们都假设 async 方法(AddAsync
在我的例子中)返回我想要返回的对象,但这里的情况并非如此;asAddAsync
只返回 ID,但我想返回添加的对象本身。
我已经重新编写了两次代码,这两个替代方案不会插入重复项:
// Alternative 1
var result = new List<Corporation>();
foreach (var name in names)
{
var foo= new Foo() { Name = name };
await AddAsync(foo);
result.Add(foo);
}
return result.ToArray();
// Alternative 2
var foos = names.Select(name => new Foo{ Name = name });
await Task.WhenAll(foos.Select(foo => AddAsync(foo)));
return foos.ToArray();
Run Code Online (Sandbox Code Playgroud)
所以我很确定错误源于我的代码,而不是AddAsync
. 但我不明白为什么第一个表现不同。虽然我不期待AddAsync
这里的内容,但我为了完成而添加它:
public static async Task AddAsync<TEntity>(TEntity entity)
where TEntity : class
{
using var scope = _scopeFactory.CreateScope();
var context = scope.ServiceProvider.GetService<MyDbContext>();
context.Add(entity);
await context.SaveChangesAsync();
}
Run Code Online (Sandbox Code Playgroud)
谁能发现为什么我的任务被执行了两次?
此查询被枚举两次, onTask.WhenAll()
和 on tasks.Select()
。
因为调用AddAsync
是在枚举期间创建的函数内部,所以它会被执行两次。
物化查询解决了这个问题:
var tasks = names.Select(async name =>
{
var foo = new Foo() { Name = name };
await AddAsync(foo);
return foo;
})
.ToList();
Run Code Online (Sandbox Code Playgroud)
请注意,您的备选方案 2 包含相同的双重枚举问题,但这次枚举只会导致Foo
不必要地创建新实例。调用AddAsync
在枚举之外。