1

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

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

        return View(gsModel);
    }

和视图模型:

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 Async="true" %>。

所以,问题:

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

2 回答 2

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 关于async构造函数的帖子。

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

    public async Task<ActionResult> Index()
    {
        var gsModel = await GetStartedModel.Create()
    
        return View(gsModel);
    }
    
于 2013-03-10T17:29:12.297 回答
0

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

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

现在来回答你的问题,

  • 绝对可以将“控制器”,“事件”和“页面”标记为异步(我不确定是否将页面标记为异步,因为我从未使用过它)并且这些方法将暂停,直到执行操作完全用那个方法写的。
  • 这就是将整个初始化逻辑封装在 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 });
}

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

(希望这可以帮助...)

于 2013-03-10T11:50:52.240 回答