我设计了以下方法来创建记录。
public Task<Guid> NotAwaited()
{
Account account = new Account();
Context.Accounts.Add(account);
Context.SaveChangesAsync();
return new Task<Guid>(() => account.Id);
}
然后,我意识到有可能在返回GUID时无法完成保存。因此,我添加了await
,这需要我用async
装饰方法签名。在此之后,我遇到了一个错误,要求使用简单的返回语法,就像这样。
public async Task<Guid> Awaited()
{
Account account = new Account();
Context.Accounts.Add(account);
await Context.SaveChangesAsync();
return account.Id;
}
我知道account.Id
部分会以某种方式转换为任务。我只是不确定如何。感觉好像是黑魔法(据我所知不是)。
是否存在隐式转换?还是我仍然不正确地执行异步调用?
答案 0 :(得分:2)
感觉就像是黑魔法
可能是sufficiently advanced to be indistinguishable from magic。
您编写C#代码,然后编译器将其拆分为多个部分,这些部分一起运行,并创建一个状态机,每次等待的异步任务完成时,状态机都会“向前移动”。如果您调试代码,则调试器会知道如何在调试器中表示“局部变量”(实际上可能是状态机类型的实例成员)并映射到原始源代码行。
因此,对于您的情况,代码可能类似于(通过Sharplab创建,请参见this gist):
[AsyncStateMachine(typeof(<Awaited>d__0))]
public Task<Guid> Awaited()
{
<Awaited>d__0 stateMachine = default(<Awaited>d__0);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<Guid>.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder<Guid> <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Awaited>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<Guid> <>t__builder;
private Account <account>5__2;
private TaskAwaiter <>u__1;
private void MoveNext()
{
int num = <>1__state;
Guid id;
try
{
TaskAwaiter awaiter;
if (num != 0)
{
<account>5__2 = new Account();
Context.Accounts.Add(<account>5__2);
awaiter = Context.SaveChangesAsync().GetAwaiter();
if (!awaiter.IsCompleted)
{
num = (<>1__state = 0);
<>u__1 = awaiter;
<>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
}
else
{
awaiter = <>u__1;
<>u__1 = default(TaskAwaiter);
num = (<>1__state = -1);
}
awaiter.GetResult();
id = <account>5__2.Id;
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<>t__builder.SetResult(id);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
}
您可以看到,调用SaveChangesAsync()
的参与方与访问Id属性的所在逻辑分支不同。现在,局部变量account
是生成的结构上的<account>5__2
字段。所使用的标识符名称中没有一个实际上是有效的C#标识符,但是在编译成的基础IL语言中有效,上面的代码是实际生成的代码的反编译C#-ish表示形式。
对Awaited()
方法的调用实际上将创建“隐藏的” <Awaited>d__0
结构的新实例(在调试模式下,它将是class
而不是struct
支持编辑并继续),并使用异步基础架构的类型来连接此状态机并运行它。
MoveNext()
。您可以看到最后一部分将结果设置为id
值,这基本上就是您的return
语句。
值得一提的是,在大多数代码中还有一个try-catch,将异常包装到任务结果中,因此您可以在代码(或从代码中调用的代码)中throw
结束安排方法的异步部分时,会创建失败的任务而不是未处理的异常。
答案 1 :(得分:0)
您可以将WHERE
视为将结果(返回值和异常)包装到HAVING
中。
与之类似,GROUP BY
解开结果(提取返回值或引发异常)。
我有一个async
intro,它会做得更详细,我建议继续使用async
best practices。附带说明,you should never use the Task
constructor。