views:

47

answers:

2

I tried to simply new up a different controller and then calling the action method. The return value is of type ViewResult and the model was created, but the View is null. I guess the context in which the runtime tries to find the correct view is wrong.

How can I achieve this?

Background

Trying to see if a template controller can use a view to both render HTML e-mail to the browser as well as render to a string to be appended to the body of an MailMessage object. The second scenario will happen in a different controller, hence the problem that calling the template controller's action does not resolve the view.

This may be similar to a unit test environment where you want to check if the view result contains the content you expected.

Thanks!

+1  A: 

Check this site, i think it's what you are looking for. http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/

David Martinez
Thanks, it's not quite what I'm looking for since it requires to pass in the model from outside.
michielvoo
A: 

michielvoo - as mentioned in the comment on OT, a few methods that may be useful if you also need to output your views or partialviews as strings (this is from my BaseController):

public static class ExtensionMethods
{
    // usage
    /*
        var model = _repository.Find(x => x.PropertyID > 3).FirstOrDefault();
        var test = this.RenderViewToString("DataModel", model);
        return Content(test);
     */
    public static string RenderViewToString<T>(this ControllerBase controller, string viewName, T model)
    {
        using (var writer = new StringWriter())
        {
            ViewEngineResult result = ViewEngines
                      .Engines
                      .FindView(controller.ControllerContext, viewName, null);

            var viewPath = ((WebFormView)result.View).ViewPath;
            var view = new WebFormView(viewPath);
            var vdd = new ViewDataDictionary<T>(model);
            var viewCxt = new ViewContext(controller.ControllerContext, view, vdd, new TempDataDictionary(), writer);
            viewCxt.View.Render(viewCxt, writer);
            return writer.ToString();
        }
    }

    public static string RenderPartialToString<T>(this ControllerBase controller, string partialName, T model)
    {
        var vd = new ViewDataDictionary(controller.ViewData);
        var vp = new ViewPage
        {
            ViewData = vd,
            ViewContext = new ViewContext(),
            Url = new UrlHelper(controller.ControllerContext.RequestContext)
        };

        ViewEngineResult result = ViewEngines
                                  .Engines
                                  .FindPartialView(controller.ControllerContext, partialName);

        if (result.View == null)
        {
            throw new InvalidOperationException(
            string.Format("The partial view '{0}' could not be found", partialName));
        }
        var partialPath = ((WebFormView)result.View).ViewPath;

        vp.ViewData.Model = model;

        Control control = vp.LoadControl(partialPath);
        vp.Controls.Add(control);

        var sb = new StringBuilder();

        using (var sw = new StringWriter(sb))
        {
            using (var tw = new HtmlTextWriter(sw))
            {
                vp.RenderControl(tw);
            }
        }
        return sb.ToString();
    }
}

may or may not be of use in this scenario, but worth a wee look..

jim
Looks good, but the action I want to call already does all the work to get the model, so I'd be duplicating that logic in the calling method, since I would need to pass the model to this RenderViewToString method.
michielvoo
michielvoo - no worries, tho see a similar question here on SO from a few months back. it proposed a similar scenario to what i described in my comment in the OT re task/service layer: http://stackoverflow.com/questions/1296680/net-mvc-call-method-on-different-controller#1304449 also, it's an old one, but you could check this out too: http://stephenwalther.com/blog/archive/2008/07/21/asp-net-mvc-tip-22-return-a-view-without-creating-a-controller-action.aspx
jim
Thanks for the links, but still, moving this to the service layer means that using the ViewEngine to render templates is no longer possible.
michielvoo