C# 链接任务和返回结果

C# 链接任务和返回结果,c#,task-parallel-library,ef-core-2.0,C#,Task Parallel Library,Ef Core 2.0,我有一个关于用TPL链接任务的问题。它必须与实体框架核心一起工作,因此我们不支持在同一数据库上下文上的并发操作 我希望尽可能地异步。以下是不带异步的代码: Context.Set<T>().Add(entity); Context.SaveChanges(); return entity; 但我认为这是不对的保存更改同步()。如果我调用:await AddAsync(myEntity),我确定保存更改同步()将完成吗?我不这么认为 所以我试图解决这个问题,但最终我得到了嵌套的任务,

我有一个关于用TPL链接任务的问题。它必须与实体框架核心一起工作,因此我们不支持在同一数据库上下文上的并发操作

我希望尽可能地异步。以下是不带异步的代码

Context.Set<T>().Add(entity);
Context.SaveChanges();
return entity;
但我认为这是不对的<返回的任务中不包含代码>保存更改同步()。如果我调用:
await AddAsync(myEntity),我确定
保存更改同步()
将完成吗?我不这么认为

所以我试图解决这个问题,但最终我得到了嵌套的任务,这些任务对用户不是很友好。可能的解决方案如下(伪代码,不编译):

返回上下文.Set()
.AddAsync(实体)
.ContinueWith(addTask=>
{
返回Context.saveChangesSync()
.ContinueWith(保存任务=>
{
返回addTask.GetAwaiter().GetResult().Entity;
});
});

有没有一种方法可以通过TPL实现这种行为?如果是:我只是在重新编写
等待
?这是一个应该在许多项目中使用的API。我想让API尽可能的高性能(至少在理论上,我正在用它来学习)。用户应在API之外使用
wait
。我想避免的是通过等待在API内部造成瓶颈。我不确定,
ContinueWith
是否不仅仅是
wait
的另一个实现。当使用
ContinueWith
而不是
wait
时,我避免的另一件事是将我的方法标记为
async

我不确定为什么
AddAsync
是实体框架核心的一部分,因为它只应该将项添加到更改跟踪器(内存中),因此它不会执行任何真正的异步操作。
我甚至想说,你不应该依赖它,因为有些驱动程序(比如MySQL官方)在调用
AddAsync
时会死锁

如果需要实际的异步实现,只需使用:

public async Task<T> AddAsync(T entity)
{
    _dbContext.Set<T>().Add(entity);
    await _dbContext.SaveChangesAsync();

    return entity;
}
公共异步任务AddAsync(T实体)
{
_dbContext.Set().Add(实体);
wait_dbContext.saveChangesSync();
返回实体;
}

忽略
AddAsync
可能是一个解决方案。原因是允许值生成器异步访问数据库,但对于我的项目,我很容易忽略这一点。我想避免
wait
,因为我认为抽象性太高了。用户应
在其代码中等待
。您知道是否可以将任务链接起来,使执行看起来像:
AddAsync
->
savechangesync
->
任务
?这在Java中是可能的,我相信在C中也是可能的。@ElMac我想你误解了
async
await
的工作原理。用户必须等待您的
AddAsync
,而不管它是否标记为
async
,是否使用
wait
。正是由于
AddAsync
返回一个
任务
这一事实使得用户必须使用
wait
来调用它。这当然是可能的,但实际上对您的场景并不有用。除了直接使用
wait
之外,还有其他处理任务的选项。例如,您可以将
等待
延迟到应用程序的后面,并且在任务的执行开始和读取结果之间具有复杂的逻辑。您甚至可以通过应用程序的多个层传播任务,而无需等待。我认为,
await
经常使用得太粗心了。@ElMac返回的真正异步任务已经处于运行(或完成)状态,因此您不能推迟它们的执行。您可以将
Wait
Wait
Result
延迟到您真正需要结果的时间点。Camilo是对的,您误解了async/await的工作原理。
async
方法中的第一个
wait
是将代表整个方法的结果任务返回给调用者的地方。任务本身可以有许多异步调用,这对调用方来说是完全隐藏的。任务通过
return
语句或异常完成。@CamiloTerevinto我为
AddAsync
SaveChangesAsync
使用
wait
解决了这个问题。我想的不是这个具体情况,而是总体情况。正如我所说,我只是想学习,我不是走“商业”的道路。无论如何,感谢您进行了有趣的讨论。“…
savechangesync()
未包含在返回的任务中”。是的
ContinueWith
返回与包含延续的原始任务不同的任务。但很快,
async/await
就是专门设计用来以一种自然(且不易出错)的方式处理连续性的。将方法标记为
异步
或不标记不会影响调用者<代码>等待
和等待是不同的事情。我的建议是:忘记原始TPL,在实现这些方法时使用
async/await
。我认为如果我
await
ContinueWith
返回的任务,那么
savechangessync()
方法不一定会完成,因为它正在其他任务中运行。它会,因为返回的任务将在原始和延续都完成时完成。您以这种方式思考的事实只是使用
async/await
:)的另一个原因,因为是的,您正在重新创建
await
。换句话说,
await
之后的代码相当于
ContinueWith
,但以更自然和编译器优化的方式。
await
不是wait。这意味着完成后继续。您可以找到许多关于
async/awai的有用解释
return Context.Set<T>()
                .AddAsync(entity)
                .ContinueWith(addTask =>
                {
                    return Context.SaveChangesAsync()
                        .ContinueWith(saveTask =>
                        {
                            return addTask.GetAwaiter().GetResult().Entity;                            
                        });
                });
public async Task<T> AddAsync(T entity)
{
    _dbContext.Set<T>().Add(entity);
    await _dbContext.SaveChangesAsync();

    return entity;
}