ActionMailer 0.6 Released

Well it’s been a few months since the last release, and I decided it was time to get cracking.  I’m sorry for the delay, but I hope you’ll agree that it was worth the wait.  So without further adieu, let’s  go over the new stuff!

Separating Components

So one of the things a lot of people have been asking for is a version of ActionMailer.Net that works without a dependency on ASP.NET MVC.  It took me a while to come up with a strategy for doing this, but I think I’ve covered all the bases.  That said, I had to move things around.  So here’s how the project is laid out now:

  • ActionMailer.Net – This project contains generic code that can be applied to either the MVC or Standalone versions of ActionMailer.Net.  This DLL is referenced by the other two projects as sort of a “Core” set of functionality.
  • ActionMailer.Net.Mvc (Nuget:  ActionMailer) – All the MVC-specific stuff has been moved into this project.  This means that MailerBase, EmailResult, and all the Html/Url helpers live in this project.  In most cases, all you’ll have to do is change your “using” statement to this new namespace, and your existing code should work just like it has been.
  • ActionMailer.Net.Standalone (Nuget:  ActionMailer.Standalone) – This project contains a standalone version of the ActionMailer.Net email rendering engine.  Right now we only support Razor views through two new classes:  RazorMailerBase and RazorEmailResult.  I’ve decided to use the RazorEngine project to power this system.  This engine supports models in the same way MVC does, but some other features (i.e.: layouts) have not been implemented, yet.  This support is still pretty basic, but it should be enough to do most things fairly well.


Installation

As I’ve linked above, there are now two Nuget packages for ActionMailer.Net.  There are also two downloadable ZIP files on the Bitbucket project page.  I recommend the Nuget packages because it’s just so easy, and it will take care of any dependencies for you.  However, I do keep the ZIP files updated, so feel free to use them as well.

Standalone:  How Does it Work?

The good news is that I’ve tried to keep the standalone usage very similar to the MVC version.  All you really need to do is inherit from RazorMailerBase and return RazorEmailResults, just like you do with MVC.  I’m sure code is more helpful, so step 1 is to create your mailer:

public class Mailer : RazorMailerBase {
    public override string ViewPath {
        get { return "Templates"; }
    }

    public RazorEmailResult Verification(User model) {
        To.Add(model.EmailAddress);
        From = "foo@bar.com";
        Subject = "Account Verification";
        return Email("Verification", model);
    }
}

You should notice that RazorMailerBase is an abstract class with only one property that you must implement, and that’s the ViewPath.  This tells ActionMailer.Net where you have stored your views.  In this case, I’ve chosen a folder called “Templates.”  You can see my first email configured in the Verification method.  Now I need to create a view in the folder configured as the ViewPath:

Hello @Model.Name,

This is a sample email.

You’ll notice that I did not specify a @model line in the Razor view.  This is because the Razor engine does not need it.  The Model object will exist, however, if you pass it to the Email method just as we did above.  This is all you need to make a view.  There’s one other important thing to note, however:  When you make a view, it needs to end in .txt.cshtml or .html.cshtml depending on the format.  The standalone version of ActionMailer.Net supports multipart messaging just like the MVC version, so we have to specify the format we wish to use.  In the example above, the view should have been named:  Verification.txt.cshtml.

The final step is to call your new Mailer class anywhere in your application like so:

var user = FindUserByID(123);
new Mailer().Verification(user).DeliverAsync();

As you can see, the RazorEmailResult has delivery methods defined.  Here I’m using DeliverAsync to send the email, but there is also a Deliver method if you’d like the call to happen synchronously.

Wrapping it Up

I hope you can see that using the standalone version of ActionMailer.Net is just as easy as the MVC version.  I’ve tried to keep things as simple as possible.  Keep in mind that RazorEngine is not as full-featured as the MVC razor engine, so more advanced features like partials, HTML helpers, and layouts just don’t work.  However, I still believe that the basic support that is enough for most use cases.  I have plans to expand the default standalone template in the future to give some email-friendly HTML helpers, just like we have in the MVC version.  Please test this out and let me know what you think.  It needs more features, for sure, but this is a good start, I think.  Cheers!

Tags: , ,

  • Cymen Vig

    It’d be really awesome to have a permanent link on the right sidebar or somewhere that wasn’t in a blog post body to the BitBucket repo! Thanks!

    Cymen

    • http://geeksharp.com Scott Anderson

      There is one in the little “About Me” box, but I suppose I could make a Projects menu or something similar.  Thanks for the suggestion.

  • Jeffrey Sogolov

    1. I think it the layouts is by far the most useful feature. Imagine dozens of templates and one has to repeat the same header, footer, etc on every template. This would be awesome fro the standalone.
    2. You have a hard dependency on system.net and the whole .net’s mail class. There are dozens of service now like Amazon SES and the framework (IMHO) needs a generic approach where one would be able to substitute any service they like.

    Thoughts?

    • http://geeksharp.com Scott Anderson

      Jeffrey,

      Thanks for the feedback!  Let me address each of the points you made:

      1. Layouts are actually fully supported in ActionMailer for MVC, however they are not currently supported in ActionMailer standalone.  I’m not opposed to adding support in the standalone library, but it’s not currently supported in RazorEngine.  This means that I’ll have to extend RazorEngine myself or wait for them to add that feature.  We’ll see how things go.  It will depend on my free time more than anything.  That said, patches pull requests are welcome if you feel like contributing! :)

      2.  System.Net is a core namespace, so I have no problem with taking a dependency on it.  You’re free to create your own IMailSender implementation (for Amazon SES or whatever other service you use).  Once you’ve implemented your own version, you can set the MailSender property on your mailer to use your custom implementation.

      If you need access to message body and other various properties in your custom IMailSender implementation, you’ll find that MailMessage object has a property called AlternateViews.  This property contains both the plain text and HTML rendered output for your message.  If you need to access the mail message body in your MailerBase itself, take a look at the OnMailSending() method.  You can override it in your mailer to get access to the generated messages before they are sent.  There’s also an OnMailSent() method which can be overridden, and it will be called after emails have been sent.

      I guess, in the end, I don’t see the benefit in completely divorcing myself from the built-in MailMessage class and System.Net namespace, because I would have to re-invent the whole process of creating an email.  It would mean a lot of code, and a lot of headaches.  The built-in classes in .NET handle this well enough, in my opinion.

      All in all, I feel like ActionMailer.Net is very extensible already.  You have complete control over your messages; and how and where they are sent through the overrides and  interfaces provided.  If you need more specific help, please let me know.  I’ll be happy to reply.  Cheers!

      • Jeffrey Sogolov

        Thanks Scott. My main gripe was the dependency on MailMessage as every service in the cloud uses their own. Regarding the layout support, I’ve already brought it up to the RazorGenerator’s David Ebbo and I hope that will be done in the upcoming weeks or months.

        • http://geeksharp.com Scott Anderson

          I’m actually using RazorEngine, not RazorGenerator (two different projects).  I have seen David’s RazorGenerator, but I haven’t taken a look at it for this project.

  • Tsarpov

    You should provide an example of using the standalone version. Where is the sample code? Also, you should explain in more detail when I would want to use the standalone version. I suspect that this is the solution to using your mailer with something like Quartz.net. I would prefer sample projects over a video anyday. Both would be the best but at least a sample project.

    • http://geeksharp.com Scott Anderson

      The Bitbucket repository has a wiki that explains how to use ActionMailer.Net quite well.  For the standalone library, this post explains the differences and how you could use it.  Is there something specifically that isn’t clear?  I’m actually working on a more formal documentation site, but I haven’t had the time to piece it together, yet.

      • Tsarpov

        My experience with the MVC version has helped but having a .NET  project available for refernece provides more details and ensures that unknowns can be answered easily. The only reason I figured out how to use the MVC version for a pro app is because of Dr Fonz’s example.
        The wiki simply leaves too much out.   A few sample projects would help a lot.
        Example: Right now,  I am trying to get the Standalone version working and the @model refernce in the view template renders error: ” model does not exist in the current context”.  I can send the email but the model does not render the data within the templates. The only way I get it to even email is to do @@model but that simply returns @@model as text within the email instead of the actual data value.

        • http://geeksharp.com Scott Anderson

          The standalone version does not need a @model line at all.  You don’t get intellisense outside of MVC anyway, so there’s no need for it.  Just create your view like you normally would, just without the @model line.  Also realize that the standalone version has no HTML helpers either.

  • Nate

    Any idea why I can send a mesages when smtp is set to pickupDirectoryLocation but not when I point it to a real mail server? This is a problem with standalone only not with MVC

    • http://geeksharp.com Scott Anderson

      Nate,

      I think it’s probably your anti-virus program blocking it.  I was experience this issue on my work PC, too, and it turns out McAfee was stopping the email from going out (even through the loopback interface).  Check your Event Viewer to see if that’s happening to you too.

      Cheers

  • Zonkzen

    Important question: is there a way to store views in e.g. database rather than creating real view files?

    • http://geeksharp.com Scott Anderson

      Right now, there is not.  However that’s something that is possible with the Standalone version, it just needs to be coded.  It’s an interesting idea, and it might be worth looking into.

      • Zonkzen

        Can I post the code to your project then?

        • http://geeksharp.com Scott Anderson

          Feel free to contribute however you’d like.  If you want to fork the project to add the functionality and then submit a pull request through BitBucket, I’d be happy to accept your contributions :)

          Cheers!
          Scott

  • Angus

    Hi Scott. I’m loving ActionMailer.net. Just one thing you may be able to help out with:

    I’ve want to
    embed an image so I do it like so:

    Attachments.Inline["corplogo.jpg"] =
    File.ReadAllBytes(HostingEnvironment.MapPath(“~/images/slide1.jpg”));

    and then in my
    view:

    img src=”cid:corplogo.jpg”

    This works fine with Windows Live Mail and MS Outlook, but my image doesn’t show in Gmail even when I click ‘display images’, I just get the missing image icon. Google does show the image as an attachment, but not in the body of the email. So I resorted to using LinkResource  in your OnMailSending override, like so:

        protected
    override void OnMailSending(MailSendingContext context) {   

                //
    here I could set context.Cancel to stop the message   

                //
    or I could see the message as context.Mail

    LinkedResource
    imagelink = new LinkedResource(HostingEnvironment.MapPath(“~/images/slide1.jpg”),
    System.Net.Mime.MediaTypeNames.Image.Jpeg);

               
    imagelink.ContentId = “imageId”;

                context.Mail.AlternateViews[0].LinkedResources.Add(imagelink);

            }

        protected
    override void OnMailSending(MailSendingContext context) {   

                //
    here I could set context.Cancel to stop the message   

                //
    or I could see the message as context.Mail

    LinkedResource
    imagelink = new LinkedResource(HostingEnvironment.MapPath(“~/images/slide1.jpg”),
    System.Net.Mime.MediaTypeNames.Image.Jpeg);

               
    imagelink.ContentId = “imageId”;

                context.Mail.AlternateViews[0].LinkedResources.Add(imagelink);

            }

    Then, it looks great in all email clients, and when i click ‘display images’ on gmail, they open. My question is, why would I opt to use inline images over using linkedresource? Why does gmail not show the inline images? And if I have to use my method, whats the most elegant way of handling this with ActionMailer.net. Thanks

  • Clubstesting1

    Thanks for creating ActionMailer.   A couple of requests:
    1) Having a display name on the To address is helpful (so people don’t recognize)
    2) I could not get InlineCSS to work, a working example with a basic MVC app would be great.

    Thanks!

  • Clubs Testing

    I have a question about web services.  Will ActionMailer views/templates I have work for a webservice call?  Or do I have to use the Standalone version?

    I want to expose our logic but don’t want to port to standalone if I can avoid it.