应该何时将操作标记为异步?

时间:2013-03-10 11:03:56

标签: c# asp.net-mvc async-await

我在mvc 4 app中有一个控制器动作:

public ActionResult Index()
    {
        GetStartedModel gsModel = new GetStartedModel();

        return View(gsModel);
    }

和ViewModel:

public class GetStartedModel
{
    public IEnumerable<SelectListItem> listA { get; set; }
    public IEnumerable<SelectListItem> listB { get; set; }

    public GetStartedModel()
    {
        TestDataWebServiceHelper service = new TestDataWebServiceHelper();
        this.GetData(service);
    }

    private async void SetData(TestDataWebServiceHelper service)
    {
        listA = await this.SetListA(service);
        listB = await this.SetListB(service);
    }

    private async Task<IEnumerable<SelectListItem>> SetListA(TestDataWebServiceHelper service)
    {
        List<String> rawList = new List<String>();
        rawList = await service.GetValuesAsync("json");
        return rawList.Select(x => new SelectListItem { Text = x, Value = x });
    }

    private async Task<IEnumerable<SelectListItem>> SetListB(TestDataWebServiceHelper service)
    {
        List<String> rawList = new List<String>();
        rawList = await service.GetValuesAsync("json");
        return rawList.Select(x => new SelectListItem { Text = x, Value = x });
    }
}

当我调用此控制器操作时,我收到以下错误:

  

此时无法启动异步操作。异步操作只能在异步处理程序或模块中启动,或者在页面生命周期中的某些事件中启动。如果在执行页面时发生此异常,请确保将页面标记为&lt;%@ Page Async =“true”%&gt ;.

所以,问题:

  1. 我应该以某种方式将控制器或操作或页面本身标记为异步以允许此模型初始化吗?
  2. 是否可以将所有初始化逻辑封装到viewmodel而不是将其弹出到控制器?
  3. 该错误的原因是什么?看起来它与WebForms有关,但我使用的是Razor引擎。

2 个答案:

答案 0 :(得分:5)

您的代码中存在两个问题:

  1. 您不应该像这样构造函数启动async void操作。实际上,您通常不应该从构造函数启动任何async操作,也不应该使用async void方法(事件处理程序除外)。

    我认为在你的情况下,async工厂方法而不是构造函数最有意义:

    private GetStartedModel()
    {}
    
    public static async Task<GetStartedModel> Create()
    {
        var service = new TestDataWebServiceHelper();
        var result = new GetStartedModel();
        listA = await result.SetListA(service);
        listB = await result.SetListB(service);
        return result;
    }
    

    有关详细信息,请参阅Stephen Cleary's post on async constructors

  2. 您还需要让控制器操作async

    public async Task<ActionResult> Index()
    {
        var gsModel = await GetStartedModel.Create()
    
        return View(gsModel);
    }
    

答案 1 :(得分:0)

关于“异步”的一些注意事项如下:

  • 需要等待来执行操作时,该方法应该只是“异步”。
  • 如果方法中没有“await”,则该方法不应声明为“async”。
  • 如果有任何事件(或其他事件处理程序)被标记为async,则返回类型应为“void”或任何特定的返回类型
  • 但如果任何其他(常规)方法应标记为“async”,则返回类型应为TaskTask< return-type >

现在回答您的问题,

  • 绝对可以标记“Controller”,“Event”&amp; “Page”as async(我是 不是很确定将页面标记为异步因为我从未使用过它) 并且通过这种方法将采取休息直到行动将是 完全用那种方法写的。
  • 这是封装整个初始化的实际系统 viewModel中的逻辑。
    • 为此制作一个Floder,将其命名为“ViewModels”并将所有viewModel代码放在该文件夹中,并在需要时使用它。

您应该将此代码放在ViewModel中:

private async void SetData(TestDataWebServiceHelper service)
{
    listA = await this.SetListA(service);
    listB = await this.SetListB(service);
}

private async Task<IEnumerable<SelectListItem>> SetListA(TestDataWebServiceHelper service)
{
    List<String> rawList = new List<String>();
    rawList = await service.GetValuesAsync("json");
    return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}

private async Task<IEnumerable<SelectListItem>> SetListB(TestDataWebServiceHelper service)
{
    List<String> rawList = new List<String>();
    rawList = await service.GetValuesAsync("json");
    return rawList.Select(x => new SelectListItem { Text = x, Value = x });
}

然后你应该在需要时给它打电话。

(希望这会有所帮助......)