【问题标题】:Rendering a Razor view as a string within a Task将 Razor 视图呈现为任务中的字符串
【发布时间】:2019-03-05 19:32:11
【问题描述】:

以下代码适用于将 Razor 视图呈现为字符串:

/// 
/// url: /api/createHtml
///
public ActionResult CreateHtml()
{
    // Heavy calculations
    MyModel myModel = new MyModel();
    myModel.Feature1 = ...;
    myModel.Feature2 = ...;
    myModel.Feature3 = ...;

    ViewData.Model = myModel;

    using (var stringWriter = new StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, "MyView");
        var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, stringWriter);
        viewResult.View.Render(viewContext, stringWriter);
        viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);

        string html = stringWriter.GetStringBuilder().ToString();

        byte[] htmlBytes = Encoding.ASCII.GetBytes(html);

        System.IO.File.WriteAllBytes(Server.MapPath("~/temp/foo.html"), htmlBytes);
    }

    return JSON(new 
    {
         error = false,
         message = "Your view is available at temp/foo.html"
    });
}

上述代码同步运行,这意味着对/api/createHtml/ 的AJAX 请求将在创建temp/foo.html 文件时结束。

我想异步执行此操作:这意味着 AJAX 请求会快速返回给用户,并带有如下消息:“您的视图将在 temp/foo.html”。然后用户必须检查文件是否准备好(通过简单地轮询临时目录[或使用其他方法,在这个问题中并不重要])

所以,当我在 Task 中尝试相同的代码时,它不起作用:

/// 
/// url: /api/createHtml
///
public ActionResult CreateHtml()
{
    new Task(() =>
    {
        // Heavy calculations
        MyModel myModel = new MyModel();
        myModel.Feature1 = ...;
        myModel.Feature2 = ...;
        myModel.Feature3 = ...;

        ViewData.Model = myModel;

        using (var stringWriter = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, "MyView");
            var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, stringWriter);
            viewResult.View.Render(viewContext, stringWriter); // <--- Problem
            viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);

            string html = stringWriter.GetStringBuilder().ToString();

            byte[] htmlBytes = Encoding.ASCII.GetBytes(html);

            System.IO.File.WriteAllBytes(Server.MapPath("~/temp/foo.html"), htmlBytes);
        }

    }).Start();

    return JSON(new 
    {
         error = false,
         message = "Your view _WILL BE_ available at temp/foo.html"
    });
}

它不起作用,因为它在viewResult.View.Render(...) 处引发异常

值不在预期范围内。

看来viewContext传递给viewResult.View.Render(...)在新线程中不再有效,如下图:ASP.NET MVC: Exception rendering view in new thread

是否有在 Task 中呈现视图的解决方法?

我知道我可以使用“RazorEngine”,这是一个免费的库,可以在没有所有控制器的情况下呈现剃刀视图,但我更喜欢使用本机代码来重新利用代码。

发布编辑:

少数答案认为我想使用“等待异步”。 我没有。我不想等待任务完成。

【问题讨论】:

  • 您是否尝试过改用Task.Run(() =&gt; { your code })?这应该只是在线程池上安排任务。如果它是一个长时间的任务,你也可以使用 TaskCreationOptions.LongRunning 选项(在这种情况下它不会使用池)
  • 我会试试的。但它和.Start() 有什么不同吗?
  • 您声明调试器无故停止。你看过你的应用程序输出吗?您是否尝试过在 VS 中启用设置以捕获所有异常?
  • 尝试在Task构造函数参数中添加try-catch块,看看是否抛出任何异常。

标签: asp.net-mvc


【解决方案1】:

嗯,你的任务可能正在做它应该做的事情,但主要威胁不是在等他。

尝试使用:

var myTask=new Task(() =>
{
    // all of your code

}).Start();

myTask.Wait();

查看更多info

最好的方法是使用async and await,因为任务应该用于异步操作,祝你好运。

【讨论】:

  • 不,该任务应该完成创建一个 .html 文件,但没有这样做。我在同步模式下尝试过,效果很好(确实创建了文件)
【解决方案2】:

异步操作方法,仅在 Web 服务器内部异步工作。异步方法的优点是它们将宝贵的线程释放回线程池,当他们不需要它时(而不是阻塞它)...... 但是 HttpRequest 仍然是同步的...... . 您的浏览器将同步等待来自异步操作方法的响应。

您需要生成一个新线程或任务来执行您的长时间运行的任务并从操作方法返回。

看看:Long Running Background Tasks in Asp.Net MVC3

这个问题也可能有帮助:Do asynchronous operations in ASP.NET MVC use a thread from ThreadPool on .NET 4

【讨论】:

  • 但我希望代码并行运行并实际返回 null 给用户请求。我不想等待它。
  • 也许我的问题不够清楚。我会稍微编辑一下。我不希望浏览器同步等待 ajax 响应。我希望 ajax 响应只是返回“嘿,任务已经在运行,稍后再回来查看您的视图”
猜你喜欢
  • 2015-08-02
  • 2012-03-03
  • 1970-01-01
  • 1970-01-01
  • 2015-02-01
  • 2013-10-19
  • 1970-01-01
  • 1970-01-01
  • 2011-01-28
相关资源
最近更新 更多