How to make a Razor generated view write to the Response.Output

Dec 22, 2011 at 9:23 PM

I have a controller action like this:

public ActionResult Index()
{
    //controller logic
    //controller logic
    //controller logic

    return View(model);
}

Currently, I have a razor view (Index.cshtml) that gets rendered for this action.  This all works fine, however I really need to be able to deploy the application WITHOUT deploying the .cshtml, so I am considering using RazorGenerator to get the job done.  I used RazorGenerator to convert the view to C# (Index.generated.cs).  Now I am trying to figure out how I can modify my controller action to use the class in Index.generated.cs for the ActionResult, instead of just returning "View()" and letting MVC search for the .cshtml file.

Here is my change to the controller action:

public ActionResult Index()
{
    //controller logic
    //controller logic
    //controller logic
    
    return RazorGeneratedView(new Index(), model);
}

Here is the "RazorGeneratedView" helper method I added:

private ActionResult RazorGeneratedView<T>(WebViewPage<T> page, T model)
{
    return new RazorGeneratedViewResult<T>(page, model);
}

And here is that RazorGeneratedViewResult class I created:

 public class RazorGeneratedViewResult<T> : ActionResult
{
    private readonly WebViewPage<T> _page;
    private readonly T _model;

    public RazorGeneratedViewResult(WebViewPage<T> page, T model)
    {
        _page = page;
        _model = model;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        _page.ViewData.Model = _model;
        _page.SetInternal("PageContext"new WebPageContext(context.HttpContext, _page, _model));
        _page.OutputStack.Push(new StreamWriter(context.HttpContext.Response.OutputStream));
        _page.Execute();
    }
}

Does this make sense?  This seems to kind of work.  Is there a better way of doing this?  I might also add that my implementation doesn't support a layout page (yet).  There is also a strange issue that I can't quite figure out: it seems the text that is output gets cut off at about 2000 characters.  I have no idea why that would be.

Can anyone help?

Coordinator
Dec 22, 2011 at 10:21 PM

Did you install the RazorGenerator.Mvc package in your application? You should be able to deploy the generated binary and have it serve the precompiled views for you (including Layout pages) without changing your controller.

Dec 22, 2011 at 10:27 PM

I did not do that because I don't wish to take a dependency on another DLL.  I would like to ship only my DLL and have it be completely self-contained.  Otherwise I would have used the "PrecompiledMvcEngine".  Does that make sense?

Coordinator
Dec 22, 2011 at 10:35 PM

Not really, I can't see why you wouldn't want to take a 3rd party dependency. But if you really are opposed to it, have a look at the RazorGenerator.Mvc project in our source tree. There's about 3 files in the project that you can use directly. You can replace the code that depends on WebActivator with a PreApplicationStartMethod and that should get you all set.

Coordinator
Dec 23, 2011 at 6:37 AM

I agree with Pranav on both points:

  • It's best not to have to change you controller code to buy into this feature, so you can easily move back and forth. This was one of the design goals.
  • If you really don't want the runtime dependency, you can just embed the code into your assembly. Which in a way is not different from what you're doing above; it's just different code :)
Coordinator
Dec 24, 2011 at 9:21 PM

PreApplicationStartMethod is just an alternate way of having startup code that doesn’t require modifying global.asax. It’s very convenient in the context of NuGet, as packages can add logic without modifying global.asax (which is difficult to automate reliably). But if it’s just your one app that you control, you can just use Application_Start directly.

As for the source code, you just need what’s in the RazorGenerator.Mvc project. I think it's just 2 files: PrecompiledMvcEngine.cs and PrecompiledMvcView.cs.