将服务器端转换为Razor

时间:2015-04-09 19:29:34

标签: asp.net-mvc razor

我正在将ASP.NET webforms应用程序升级/修改为MVC5 - 理想情况下是Razor引擎(允许自动移动布局页面切换,并利用MVC功能)。< / p>

我们有数百个服务器端包含,全部名为* .inc。他们的绝对gobs。应用程序非常庞大,以至于我们必须逐页转换页面 - 没有办法一次完成所有操作。 (为了了解这个项目有多大以及它已经存在了多长时间,我们迁移到TFS时VSS中的整个源代码控制历史记录大约是10GB。这包括很多媒体文件,但仍然是。&#39 ;大,但它不会消失。)

有没有办法可以转换这些#!include调用RenderPartial()或RenderAction()调用而不用将每个文件重命名为.cshtml?

复制包含文件是一个不好的建议,因为这会破坏DRY。

理想情况下,我希望一个部分视图可以处理这个问题 - 我想它会将文件名作为参数,而且这是一个单行的内容,所以我可以培养每个人轻松转换这些。

我还想要一些文件级缓存。只需加载磁盘和输出;将它转储到输出流中对小型网站来说很不错......但这不是一个小网站。

我们有一个ascx文件,现在可以为许多这些包含进行缓存 - 但我不知道如何在Razor中编写相应的文件。

编辑1:这些大部分都是非代码包含的,但我确定用户控件定义已经陷入了一些困境。我必须重写那些,所以假设答案是&#34;这些是纯HTML,没有代码&#34;

编辑2:我们遇到包含文件嵌套的情况(我发誓不是这样)。包含文件可以包含其他文件。我有一个缓存ASCX控件来递归处理这个,所以这是移植代码的问题。

编辑3:任何可能的解决方案也必须适用于布局cshtml文件。我们已经包含在现有的母版页中。

3 个答案:

答案 0 :(得分:6)

在我的头脑中,我可以想到解决这个问题的四种方法。

注意:我没有过多地使用缓存机制,因为这个设计决定超出了这个问题。


创建一个控制器/动作来为您呈现包含:

public class IncludeController : Controller
{
    // GET: Render
    public ActionResult Render(string path)
    {
        return new IncludeResult(path);
    }
}

public class IncludeResult : ActionResult
{
    private string _path { get; set; }

    public IncludeResult(string path)
    {
        _path = path;
    }
    public override void ExecuteResult(ControllerContext context)
    {
        // possibly check the cache here, or let MVC handle cache at the action level
        // get the contents of the include file output to the response
        context.HttpContext.Response.Write(File.OpenRead(_path));
    }
}

在视图中

@Html.Action("Render", "Include", new {path = "~/Includes/test.inc"})

注意:尝试使用FilePathResult,它似乎返回奇数字符,因此自定义IncludeResult


创建HtmlHelper扩展程序:

public static class HtmlExtension
{
    public static MvcHtmlString RenderInclude(this HtmlHelper helper, string path)
    {
        // check the cache
        string output = File.ReadAllText(path);
        return new MvcHtmlString(output);
    }
}

在视图中

@Html.RenderInclude("~/includes/test.inc")

我认为这个选项适合你想要做的最好,但是从最纯粹的方法来看,这可能不符合MVC做事的方式&#39;


扩展RazorViewEngine以查找包含文件:

我没有对这个问题进行过深入探讨,并且可能对你想要达到的目标有点过分,但以下博客中有一些代码示例可以帮助你:http://weblogs.asp.net/imranbaloch/view-engine-with-dynamic-view-location

然后您可以在视图中使用标准Html.Partial代码

@Html.Patial("~/includes/test.inc");

将包含内容公开在视图模型的属性中:

这似乎是最麻烦的,但它仍然是一种选择。

public class ViewModel
{
    public MvcHtmlString IncludeContents { get; set; }
    ....

    private GetIncludeContent(string path)
    {
        IncludeContents = new MvcHtmlString(File.ReadAllText(path));
    }
}

然后在视图中

@model.IncludeContents

答案 1 :(得分:2)

我强烈推荐:

编辑您的网络配置并添加(BuildProviders信息):

<system.web>
  <compilation debug="true" targetFramework="4.5">
    // Start (Build Provider Information)
    <buildProviders>
      <add extension=".inc" 
           type="System.Web.WebPages.Razor.RazorBuildProvider, System.Web.WebPages.Razor"/>
    </buildProviders>
    // End (Build Provider Information)
  </compilation>
</system.web>

这为运行时引擎提供了构建INC文件的构建提供程序。如果它们是纯HTML,那么我们只需要注册一些东西,这样MVC就会包含它们。

创建新的视图引擎:

public class CustomViewEngine : RazorViewEngine
{
    public CustomViewEngine()
    {
        PartialViewLocationFormats = new[]
        {
            "~/Views/{1}/{0}.cshtml",
            "~/Views/{1}/{0}.vbhtml",
            "~/Views/Shared/{0}.cshtml",
            "~/Views/Shared/{0}.vbhtml",
            "~/Include/{0}.inc"  // Path to your include files
        };

        FileExtensions = new[]
        {
            "cshtml",
            "vbhtml",
            "inc",
        };
    }
}

注册您的视图引擎:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        // Need to let the RazorCodeLanguage it can compile .inc files
        RazorCodeLanguage.Languages.Add("inc", new CSharpRazorCodeLanguage());
        // Need to register the extension with the WebPage Http Handler
        WebPageHttpHandler.RegisterExtension("inc");

        // Create the engine
        var viewEngine = new CustomViewEngine();
        // Clear unused Engines
        ViewEngines.Engines.Clear();
        // Add only the modified razor engine
        ViewEngines.Engines.Add(viewEngine);
    }
}

\Views\Web.config复制到\Include\Web.Config,让视图引擎了解视图的来源。

文件\Include\Test.inc包含:

<div>Some Data</div>

现在您只需添加:

@Html.Partial("Test")

或(更高效)

@{Html.RenderPartial("Test");}

现在您的INC文件已完全MVC化。这意味着任何MVC缓存控制等都可以像任何.cshtml文件一样正常工作。

<强>加成

由于我们将您的\Include\*.inc迁移到\Views\Shared\*.cshtml文件,因此我们直接绑定到RazorViewEngine,您必须修改任何{ {1}}因为它先查看共享然后包含。一旦你没有更多的包含文件并且网站继续工作,你最终可以删除所有内容(我推荐)。

答案 2 :(得分:0)

根据Brent的第二个选项,我使用WebClient下载HTML字符串,而不是从磁盘加载它。我们有一些嵌套的SSI,因此从磁盘加载并不是那么容易。

public static HtmlString RenderInclude(this HtmlHelper helper, string path)
{
   WebClient client = new WebClient();
   string url = string.Format("{0}://{1}{2}{3}", 
      HttpContext.Current.Request.IsSecureConnection ? "https" : "http", 
      HttpContext.Current.Request.Url.Host, 
      HttpContext.Current.Request.Url.Port == 80 ? "" : ":" + HttpContext.Current.Request.Url.Port.ToString(), 
      path);

  string html = client.DownloadString(url);

  return new HtmlString(html);
}

然后在剃刀中:

@Html.RenderInclude("/includes/test.inc")

也许没那么快,但我需要一个临时解决方案来让我们的嵌套SSI在迁移时工作。