【问题标题】:When should an action be marked as async?何时应将操作标记为异步?
【发布时间】:2013-03-10 11:03:56
【问题描述】:

我在 mvc 4 应用中有一个控制器操作:

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 });
    }
}

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

此时无法启动异步操作。异步操作只能在异步处理程序或模块内或在页面生命周期中的某些事件期间启动。如果在执行 Page 时发生此异常,请确保将 Page 标记为 。

所以,问题:

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

【问题讨论】:

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


    【解决方案1】:

    你的代码有两个问题:

    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);
      }
      

    【讨论】:

    • Create() 是否应该返回Task&lt;GetStartedModel&gt;
    【解决方案2】:

    “异步”需要注意以下几点:

    • 只有当它需要等待由其中编写的代码执行的操作时,该方法才应该是“异步”的。
    • 如果方法中没有“等待”的内容,则不应将该方法声明为“异步”。
    • 如果有任何事件(或事件处理程序)被标记为异步,则返回类型应为“void”或任何特定的返回类型
    • 但如果任何其他(通用)方法应标记为“异步”,则返回类型应为TaskTask&lt; return-type &gt;

    现在回答您的问题,

    • 绝对可以将“控制器”、“事件”和“页面”标记为异步(我是 不确定是否将页面标记为异步,因为我从未使用过它) 并且通过这些方法将休息,直到行动将是 完全用该方法编写。
    • 这就是封装整个初始化的实际系统 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 });
    }
    

    然后你应该在需要时调用它。

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

    【讨论】:

    • 你不能有Task&lt;&gt;,我想你的意思是Task
    • 是的,我只是说……对不起
    • 好的,解释清楚了,但我仍然不明白为什么在创建 GetStartedModel 的新实例时会收到该错误。
    猜你喜欢
    • 2017-10-21
    • 2023-03-16
    • 2021-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-24
    • 2014-11-27
    相关资源
    最近更新 更多