Jason
Jason

Reputation: 11615

ControllerContext and ViewData Outside Scope of Controller - MVC3 C#

I am rendering PartialViews/Models with the method below in order to template emails that get sent out.

I am using the code below to convert the partial and model into an html string that I can pass to my email sending code.

public class BaseController : Controller
{
    public string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            throw new ArgumentException("No View Path Provided.");

        ViewData.Model = model;

        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }
}

Currently, this lives in the BaseController, I would like to move it out into a helper method so that I can move my email model building out/sending out of the controller too.

The problem is I don't have access to ViewData/TempData/ControllerContext

I can new up a ControllerContext but I don't know what to do about ViewData/TempData.

This is how I would use what I have in the Controller currently:

//Do Stuff in Controller

var html = RenderPartialViewToString("~/Views/Mail/_ForgotPassword.cshtml", new MailModel { Username = "Skrillex", SomethingElse = "foo" });

//Send the Email

Upvotes: 5

Views: 5295

Answers (3)

KevinB
KevinB

Reputation: 1407

I have used 2 methods for Razor rendering emails that need to be sent out side of a controller... first I passed the ControllerContext to my service layer which worked as expected, but not ideal.... Often my services are used by command line apps or compiled into a Windows Service where there is no ControllerContext available... On my second attempt I used this Razor render engine: http://razorengine.codeplex.com/ or https://github.com/Antaris/RazorEngine

Upvotes: 0

Sal Zaki
Sal Zaki

Reputation: 41

You can create an instance or static property of BaseController in the helper class as shown below,

public sealed class Helper {
///Gets or sets BaseController
public BaseController { get; set; }
#region "Constructors"
/// <summary>
/// Initialises a new instance of the <see cref="Helper" /> class.
/// </summary>
public Helper() : base() {

}
/// <summary>
/// Initialises a new instance of the <see cref="Helper" /> class.
/// </summary>
public Helper(BaseController baseController) : this() {
   this.BaseController = baseController;
}
#endregion
public void SendEmail(){
   // Here you can call your RenderPartialViewToString from the BaseController
 var m_RenderPartialViewToString = this.BaseController.RenderPartialViewToString( .......);
}}

Regards. Hope this helps.

Upvotes: 0

Aliostad
Aliostad

Reputation: 81680

I think you are on the right track, but the problem is your eagerness to complete separation, it is rather too eager.

You are using Razor view engine to render your rich text HTML email. A very noble approach. However, this means that you will be very close to your presentation layer and running this from outside a controller - in my view - does not make a lot of sense.

I believe you need to make (if not already made):

  • Your email Razor view as strongly typed
  • Let the rendering called in the controller as usual
  • Rendering would be as simple as passing the model to the Render method
  • Take out the building of your email model out to the helper you wish. This would not require any presentation layer logic and as such oblivious to it.

So the point is, calling of the rendering does not need to go out of the controller, building of the email model should.

Now if you are doing all of that it means I have not understood your question and requires more explanation.

Upvotes: 2

Related Questions