Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacement for ITemplate API #238

Closed
Suchiman opened this issue Feb 12, 2015 · 6 comments
Closed

Replacement for ITemplate API #238

Suchiman opened this issue Feb 12, 2015 · 6 comments
Labels

Comments

@Suchiman
Copy link

As discussed in #225 you are deprecating direct access to ITemplate.
I'm using RazorEngine for email templates and after upgrading to 3.5 i couldn't find a solution to my problem using the migration guide or by looking at non obsoleted APIs.
My code currently looks like:

public class EmailContext<T> : TemplateBase<T>
{
    public string Destination { get; set; }
    public string Subject { get; set; }
}

public static Task SendEmailAsync<T>(string templateName, string destination, T model)
{
    ITemplate template = Razor.Resolve(templateName, model);

    EmailContext<T> context = (EmailContext<T>)template;
    context.Destination = destination;

    StringWriter writer = new StringWriter();
    template.Run(new ExecuteContext(), writer);
    string body = writer.ToString();

    MailMessage msg = new MailMessage();
    msg.To.Add(new MailAddress(context.Destination));
    msg.Subject = context.Subject;
    msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html));

    SmtpClient smtpClient = new SmtpClient();
    return smtpClient.SendMailAsync(msg);
}

which allows me to write strongly typed and IntelliSense supported email templates like:

@model HelloWorldModel
@{
    Layout = "CI";
    Subject = "Hello World";
}
Hello @Model.Name,<br/>
this is a test email...

Basically it's about having the subject property on the template base independent from the model and always being present which could be possibly pre-set by the code before calling the template and read back after executing the template in case it has been set to something by the code that called the template.
So far this still works but i'm worried once you remove the API, have you any ideas how i could solve this or any solution in a future version?

@matthid
Copy link
Collaborator

matthid commented Feb 12, 2015

From a first quick look I would say you can use the same solutions as #225.
I would suggest the ViewBag approach, IE use something like

    public string Subject { get { return Viewbag.Subject } }

and assign the Subject property to the viewbag. You can then just use the Run method directly with the viewbag instead of calling Resolve and Run manually.

Edit: The same holds for the Destination property

@Suchiman
Copy link
Author

I must have missed that one but it didn't work.
The values i pass in arrive in the template but values set in the template won't come back.
At a first glance it looks like this is because you are making a copy of the ViewBag that gets passed in

public virtual ExecuteContext CreateExecuteContext(DynamicViewBag viewBag = null)
{
    var context = new ExecuteContext(new DynamicViewBag(viewBag));
    return context;
}

@matthid
Copy link
Collaborator

matthid commented Feb 12, 2015

ah I didn't saw that you are trying to get data OUT of the template. I never saw somebody using templates this way. Still I think the same workaround can be used (it's just a little more complex):

class CustomDataHolder {
    public string Destination { get; set; }
    public string Subject { get; set; }
}
// In the custom TemplateBase class:
    public string Subject { get { return Viewbag.DataHolder.Subject; }; set { Viewbag.DataHolder.Subject = value; } }

// Your code
public static Task SendEmailAsync<T>(string templateName, string destination, T model)
{
    var holder = new CustomDataHolder ();
    dynamic viewbag = new DynamicViewBag();
    viewbag.DataHolder = holder;
    holder.Destination= destination;
    var body = Engine.Razor.Run(templateName, typeof(T), model, (DynamicViewBag)viewbag);

    MailMessage msg = new MailMessage();
    msg.To.Add(new MailAddress(holder.Destination));
    msg.Subject = holder.Subject;
    msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html));

    SmtpClient smtpClient = new SmtpClient();
    return smtpClient.SendMailAsync(msg);
}

@Suchiman
Copy link
Author

Ok 😑 i managed it to workaround that limitation but it starts getting ugly 😅

public class UndercoverEmailContext
{
    public string Destination { get; set; }
    public string Subject { get; set; }
}

public class EmailContext<T> : TemplateBase<T>
{
    public string Destination
    {
        get
        {
            return ViewBag.Context.Destination;
        }
        set
        {
            ViewBag.Context.Destination = value;
        }
    }
    public string Subject
    {
        get
        {
            return ViewBag.Context.Subject;
        }
        set
        {
            ViewBag.Context.Subject = value;
        }
    }
}

public static Task SendEmailAsync<T>(string templateName, string destination, T model)
{
    var context = new UndercoverEmailContext { Destination = destination };
    dynamic viewBag = new DynamicViewBag();
    viewBag.Context = context;

    string body = Engine.Razor.RunCompile(templateName, typeof(T), model, (DynamicViewBag)viewBag);

    MailMessage msg = new MailMessage();
    msg.To.Add(new MailAddress(context.Destination));
    msg.Subject = context.Subject;
    msg.AlternateViews.Add(AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html));

    SmtpClient smtpClient = new SmtpClient();
    return smtpClient.SendMailAsync(msg);
}

EDIT: damn you were 25 seconds faster 😄

@Suchiman
Copy link
Author

Anyway, thank you for your help, this solves my problem.

@matthid
Copy link
Collaborator

matthid commented Feb 12, 2015

Yeah the ugliness does bring some benefits though: If you ever want to switch to the Isolation API, you just need to decide how you want to serialize your UndercoverEmailContext (MarshalByRef in this case) and you are ready to go, while there is no (simple) way to make your initial code working in an isolation scenario.

So it's not that ugly if you think about it this way :)

Anyway, glad your problem is solved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants