Rui Rodrigues Rui Rodrigues - 9 months ago 114
C# Question

Implement MVC ViewRenderer in a .NET console application

I added a newsletter feature to my MVC 5 project to send constructed mails to all subscribed users.

I’ve a class with

public async Task<NewsletterLogResult> SendNewsletters(int? id = null, ControllerContext ControllerContext = null)
being called from an action
public async Task<ActionResult>SendMail(int id)


SendNewsletters
implements
Viewrenderer
, something like this
cBody = ViewRenderer.RenderPartialView(ViewRenderer.TemplatePath + "Newsletters/" + record.Newsletter_Template + "Body.cshtml", record, ControllerContext);


public class ViewRenderer
{
protected ControllerContext Context { get; set; }

public const string TemplatePath = "~/Views/Templates/";

public ViewRenderer(ControllerContext controllerContext = null)
{
// Create a known controller from HttpContext if no context is passed
if (controllerContext == null)
{
if (HttpContext.Current != null)
controllerContext = CreateController<EmptyController>().ControllerContext;
else
throw new InvalidOperationException(
"ViewRenderer must run in the context of an ASP.NET Application and requires HttpContext.Current to be present.");
}
Context = controllerContext;
}

public string RenderViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, false);
}

public void RenderView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, false);
}

public string RenderPartialViewToString(string viewPath, object model = null)
{
return RenderViewToStringInternal(viewPath, model, true);
}

public void RenderPartialView(string viewPath, object model, TextWriter writer)
{
RenderViewToWriterInternal(viewPath, writer, model, true);
}

public static string RenderView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}

public static void RenderView(string viewPath, TextWriter writer, object model,
ControllerContext controllerContext)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}

public static string RenderView(string viewPath, object model,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderViewToString(viewPath, model);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
return null;
}

public static void RenderView(string viewPath, object model, TextWriter writer,
ControllerContext controllerContext,
out string errorMessage)
{
errorMessage = null;
try
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderView(viewPath, model, writer);
}
catch (Exception ex)
{
errorMessage = ex.GetBaseException().Message;
}
}

public static string RenderPartialView(string viewPath, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
return renderer.RenderPartialViewToString(viewPath, model);
}

public static void RenderPartialView(string viewPath, TextWriter writer, object model = null,
ControllerContext controllerContext = null)
{
ViewRenderer renderer = new ViewRenderer(controllerContext);
renderer.RenderPartialView(viewPath, model, writer);
}

protected void RenderViewToWriterInternal(string viewPath, TextWriter writer, object model = null, bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);

if (viewEngineResult == null)
throw new FileNotFoundException();

// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;

var ctx = new ViewContext(Context, view,
Context.Controller.ViewData,
Context.Controller.TempData,
writer);
view.Render(ctx, writer);
}

private string RenderViewToStringInternal(string viewPath, object model,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(Context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(Context, viewPath, null);

if (viewEngineResult == null || viewEngineResult.View == null)
{
//throw new FileNotFoundException(Resources.ViewCouldNotBeFound);
throw new Exception("Can't find view.");
}

// get the view and attach the model to view data
var view = viewEngineResult.View;
Context.Controller.ViewData.Model = model;

string result = null;

using (var sw = new StringWriter())
{
var ctx = new ViewContext(Context,
view,
Context.Controller.ViewData,
Context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}

return result;
}

public static T CreateController<T>(RouteData routeData = null, params object[] parameters)
where T : Controller, new()
{
// create a disconnected controller instance
T controller = (T)Activator.CreateInstance(typeof(T), parameters);

// get context wrapper from HttpContext if available
HttpContextBase wrapper = null;
if (HttpContext.Current != null)
wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
else
throw new InvalidOperationException(
"Can't create Controller Context if no active HttpContext instance is available.");

if (routeData == null)
routeData = new RouteData();

// add the controller routing if not existing
if (!routeData.Values.ContainsKey("controller") && !routeData.Values.ContainsKey("Controller"))
routeData.Values.Add("controller", controller.GetType().Name
.ToLower()
.Replace("controller", ""));

controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
return controller;
}

}

public class EmptyController : Controller
{
}


This is working perfectly in MVC.

Like running a newsletter sending task from a web application is not a good practice, I want to implement this feature in a Windows service. In order to facilitate I’m testing that in a Consoleapp, calling from here
SendNewsletters
.
But in Consolapp, like in WIndowsService, I don’t have HttpContext, so, it’s possible to create a fake HttpContext, preferably with access to MVC routing (my razor view implements routing helpers,
url.action
)?

Answer Source

I solved this problem storing in the database, in addition, the result of viewrendered. This information is processed in the MVC application, so I do not need HttpContext or any routing.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download