我想以事务方式将更改保存到数据库中的实体Person
列表中。我已经实现了该功能,但是我不知道是否必须将await Task.WhenAll(tasks);
包装在TransactionScope中,或者它已经足够我的代码来获取它了。
public class MyService {
public MyContext Context { get; }
public MyService(
IDatabaseInitializer<MyContext> initializer
) {
Context = new MyContext(initializer);
}
public async Task<int> AddOrUpdateDataAsync(IEnumerable<Person> persons)
{
List<Task> tasks = new List<Task>();
try
{
foreach (Person person in persons)
{
person.Status = Status.Finish;
person.Changed = DateTime.Now;
person.Role = Role.Worker;
MyContext.Persons.AddOrUpdate(person);
tasks.Add(await MyContext.SaveChangesAsync(););
}
await Task.WhenAll(tasks);
return 1;
}
catch (EntityCommandExecutionException ex)
{
return 0;
}
}
}
答案 0 :(得分:2)
除非您完全确定自己在做什么,否则我建议您不要通过返回0来处理异常。我总是更喜欢在最外层堆栈处理异常并记录异常,然后采取适当的措施。并且,如果可能的话,我建议您使用错误处理中间件(这取决于所使用的框架,所使用的方法以及具体情况)。
另一方面,您不需要一个个更新每个对象。完成所有操作后,您可以调用SaveChangesAsync
。
try
{
foreach (Person person in persons)
{
person.Status = Status.Finish;
person.Changed = DateTime.Now;
person.Role = Role.Worker;
MyContext.Persons.AddOrUpdate(person);
}
var affectedRows = await MyContext.SaveChangesAsync();
return (int)(persons.Count == affectedRows);
}
catch (EntityCommandExecutionException ex)
{
return 0;
}
答案 1 :(得分:1)
tasks.Add(await MyContext.SaveChangesAsync(););
应该没有等待时间
tasks.Add(MyContext.SaveChangesAsync());
您的代码开始工作。 (await表达式返回异步结果,不带await返回将在以后执行的任务) 但是如前所述,您不应该每次将实体添加到上下文中时都执行SaveChangesAsync(),因为它可能对所有添加的实体都执行一次。
答案 2 :(得分:0)
其他答案都是正确的,但是无论SaveChangesAsync
是否是原子的,都不会解决您的问题。
根据文档,对于EF6
和'EF Core`,您的问题的答案都是“是”:
默认情况下,如果数据库提供程序支持事务,则一次调用SaveChanges()的所有更改都将应用到事务中。如果任何更改失败,那么将回滚事务,并且不会将任何更改应用到数据库。这意味着SaveChanges()可以保证完全成功,或者在发生错误时不修改数据库。
在所有版本的Entity Framework中,每当执行SaveChanges()在数据库上插入,更新或删除时,该框架都会将该操作包装在事务中。此事务仅持续很长时间以执行操作,然后完成。当您执行另一个这样的操作时,将开始新的事务。