不确定Guid类型的值如何转换为Task <Guid>

时间:2019-06-13 19:35:51

标签: c# .net asynchronous .net-core async-await

我设计了以下方法来创建记录。

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部分会以某种方式转换为任务。我只是不确定如何。感觉好像是黑魔法(据我所知不是)。

是否存在隐式转换?还是我仍然不正确地执行异步调用?

2 个答案:

答案 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

相关问题