ASP.NET Email Strategy Pattern

by admin 22. September 2008 18:46

Email Sometimes when building websites, we need to generate email more than once. For example a site could have a contact form and a user-filled form, both generating email to the site owner. You don't need to be able to spout the name of every software pattern verbatim, to recognize the duplication here and the need to somehow factor it out.

We typically use the strategy pattern where we might have several different implementations of something and want to abstract out the common functionality. The strategy is also known as the provider pattern and I prefer this term. An interface is a good candidate data structure to use here because we can stipulate that a client class must implement a mailing method, and we can leave the details to that class. Let's look at some code...

Typical Scenario

Here are some typical Web.config email settings and code to send an email:

<appSettings>
    <add key="ToEmailAddr" value="info@mysite.com" />
    <add key="FromEmailAddr" value="info@mysite.com" />
</appSettings>

<system.net>
    <mailSettings>
        <smtp from="info@mysite.com">
            <network host="smtp.mymailserver.net" port="25" />
        </smtp>
    </mailSettings>
</system.net>

 

using System.Configuration;
using System.Net.Mail;
using System.Web.Configuration;

System.Configuration.Configuration config
  = WebConfigurationManager.OpenWebConfiguration(base.Request.ApplicationPath);
AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string toAddress = appSettings.Settings["ToEmailAddr"].Value;
string fromAddress = appSettings.Settings["FromEmailAddr"].Value;

SmtpClient smtpClient = new SmtpClient();

MailMessage message = new MailMessage();
message.IsBodyHtml = false
message.Priority = MailPriority.High;
message.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;

try
{
    message.Subject = "Subject: " + this.subjectBox.Text;
    message.Body = "Sender: " + nameBox.Text.Trim() + "\n";
    message.Body += "Street: " + streetBox.Text.Trim() + "\n";
    message.Body += "City: " + cityBox.Text.Trim() + "\n";
    message.Body += "State: " + stateDropDown.SelectedValue
        + " " + zipBox.Text.Trim() + "\n";
    message.Body += "Email: " + emailBox.Text.Trim() + "\n";
    message.Body += "\n\n" + this.messageBox.Text + "\n\n";

    smtpClient.Send(fromAddress, toAddress, message.Subject, message.Body);
}
catch (Exception ex)
{
    // Log error
}

 

Strategy Pattern

While this particular email example will not save us a whole lot of typing, it does enable us to implement the email functionality differently for each client class that uses it. This may be useful if we had to send mail through several different servers; in such a case we might also want to configure our settings in code, or factor out our Web.config sections. Factoring out parts of your Web.config is another topic and can be used to greatly simplify deployment.

interface IGenerateMail
{
    // Classes using this interface must implement this method
    void SendMail(string to, string from, string subject, string body);
}

 

// Implementing the Strategy Pattern
public class RealtySMTPMailer : IGenerateMail
{
    //(string from, string to, string subject, string message)
    public void SendMail(string from, string to, string subject, string message)
    {
        MailMessage realtyMessage = new MailMessage(from, to, subject, message);
        SmtpClient smtpClient = new SmtpClient();

        realtyMessage.IsBodyHtml = false;
        realtyMessage.Priority = MailPriority.High;
        realtyMessage.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;

        smtpClient.Send(from, to, subject, message);
    }
}

 

// Snippets from revised contact form
...
private RealtySMTPMailer mailProvider;

public Contact()
{
    mailProvider = new RealtySMTPMailer();
}

public Contact(RealtySMTPMailer mailProvider)
{
    this.mailProvider = mailProvider;
}
...

// Send button event handler
System.Configuration.Configuration config
  = WebConfigurationManager.OpenWebConfiguration(base.Request.ApplicationPath);
AppSettingsSection appSettings = (AppSettingsSection)config.GetSection("appSettings");

string toAddress = appSettings.Settings["ToEmailAddr"].Value;
string fromAddress = appSettings.Settings["FromEmailAddr"].Value;

StringBuilder mailBody = new StringBuilder();

mailBody.Append("From:" + " " + nameBox.Text.Trim() + "\n");
mailBody.Append("Address:" + " "
    + streetBox.Text.Trim() + ", "
    + cityBox.Text.Trim() + ", "
    + stateDropDown.SelectedValue + " "
    + zipBox.Text.Trim() + "." + "\n");
mailBody.Append("Email:" + " " + fromAddress + "\n\n");    
mailBody.Append("Message:" + "\n" + messageBox.Text.Trim());

mailProvider.SendMail(fromAddress, toAddress, subjectBox.Text.Trim(), mailBody.ToString());

 

We can now implement email functionality anywhere in the site and change that implementation without breaking code elsewhere. You might notice some refactoring going on in the last snippet; I substituted a string builder to construct the email, as it is more efficient. Remember, when refactoring you must resist the temptation to re-code the logic.

ASP.NET Email Resources

Sending Email with System.Net.Mail - Scott Guthrie

Sending Email in a Development Environment without an SMTP Server

Sending Email from ASP.NET using your Gmail Account

kick it on DotNetKicks.com   PHP, ASP, .NET, JSP Resources, Reviews




Footer I searched for a long time for a simple CSS solution to the eternal "sticky footer" problem, where the footer goes down to the end of the page. Up to now I have resorted to JavaScript to achieve this, which is not really the most elegant solution. It should be simple, right?

The other day, I stumbled upon what looked like a basic HTML/CSS solution. I wanted CSS that would push the footer to the bottom of the page, irrespective of content height, in an ASP.NET Master Page setup. Thanks to Ryan Fait for this.

It was straightforward enough to get it working for IE7. Firefox 3.0 didn't play nice at first until I moved the wrapper and footer divs out to the master page. Feel free to download the sample below and try it out for yourself - VS 2008 solution. Ignore the red wigglies in the master page. It has been tested in IE7, Firefox 3.0 and Chrome 0.2.149.29.

CSS_Sticky_Footer.zip (19.91 kb)

kick it on DotNetKicks.com  PHP, ASP, .NET, JSP Resources, Reviews

Tags: ,

ASP.NET | CSS



BlogEngine 1.4.5 and IIS7

by agrace 6. September 2008 15:57

Blog Up to now I have been using the old 1.2 version of BlogEngine and would pop in the 1.3 dll manually after deploying to fix a known security hole. I finally decided to upgrade to the latest 1.4.5 version last night. I use the SQL Server provider and usually download the source code version to my machine.

When using VS 2005 and IIS5 locally, it was easy to configure the BlogEngine.Web folder as the virtual directory. My physical path would be /CodersBarn/BlogEngine.Web and I would just point my browser to localhost. Since upgrading to VS 2008 and IIS7, this is a whole new ball game. When using the new IIS7, you will typically end up with the following under your wwwroot folder:

localhost/app1
localhost/app2
localhost/app3, etc.

You will notice that there is an extra node in the path in IIS7. The good news is that we don't have to continually go into IIS and change localhost when switching between projects. The bad news is that this causes problems with apps migrated up from the older IIS. Or am I missing something fundamental here?...

When migrating your ASP.NET 2.0 applications to IIS7, as when one moves up to VS 2008, you may also experience many of your links breaking. This is a separate issue relating to ASP.NET rebasing. If you have a link like this:

<img src="/images/mypic.jpg" alt="My Pic" />

Try changing it to:

<img src="~/images/mypic.jpg" alt="My Pic" runat="server" />

 

The ASP.NET runtime will now correctly resolve it, thanks to the addition of the runat="server". I had a blog posting on this very subject half-prepared and I plan to come back to this subject soon. In the meantime, you can find some information on K.Scott Allen's blog posting under "URL Rebasing in Master Pages"; scroll down the page to find it.

Back to BlogEngine. I have installed the full source code in my wwwroot folder many times in the past without any real hitches. This way, I can go in, tweak some code and re-deploy fairly quickly. I usually follow Al Nyveldt's BlogEngine screencasts and adjust the steps for my particular circumstances. In Al's "Installing BlogEngine.NET 1.4" screencast, he downloads the "Web" (executable) version. Normally, I follow along except that I would download the "Source" version. To make a long story short I had problems configuring the localhost in IIS7 and started getting the "It is an error to use a section registered as allowDefinition='MachineOnly' beyond machine.config" error (typical of virtual directory not correctly created). I also experienced the breaking image links described above although this was probably a side effect. FYI, Al's screencasts are the only real source of guidance out there for working with BlogEngine that I have been able to find. The problem here was with the user!

IIS7 Settings

 

FYI: BlogEngine source download is a solution with a core Web Application project and a Web Site project consisting of the main deployable part. I hate Web Site projects; they should be stamped out!!

In the end, I decided to follow Al's steps to the letter. I reckoned I could always download the source version to another folder later, make any code tweaks needed, and pop the updated core dll into the working bin folder. Even if you already have an older version of the blog engine with your own branding, your initial goal will still be to get the out-of-the-box site working with the standard theme. You can copy in your own theme folder later, after you have everything else working. The trick here is to take it slowly. Once the site is running and you have the new user account set up, you can look to updating the SQL functionality. This will involve running a db script which you can find in the setup folder of the download and running it in SQL Server Management Studio. Then all you have to do is update your Web.Config file and this is outlined in this screencast.

Add User

 

I then started to get the following error message:

Parser Error Message: Could not load the assembly 'App_Web_045byxd7'.

So, I relaxed the permissions on the project folder in @"\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files" folder enough to let me delete the temp files. This still didn't fix it. Then I realized that it was picking this up from the original master page in my themes folder which I had copied in:

<%@ master language="C#" autoeventwireup="true" inherits="site, App_Web_045byxd7"%>

 

So, I changed this and saved it back as follows:

<%@ Master Language="C#" AutoEventWireup="true" CodeFile="site.master.cs" Inherits="site" %>

 

Somehow this was lost in the copy and paste process. This finally compiled on the fly and worked locally. I still had to update some of the controls in my side navigation bar. For some reason, I can never get the newer TagCloud control to render correctly with my theme so I just grab a copy of the older one. The newer one lists all the tags on separate lines and it takes up a huge amount of space plus the weighting doesn't work. I see that the disclaimer still insists on rendering every word in caps. The blog roll links are still set to open in the same window and we still can't edit them in admin. Also, the archive page is a total mess when used with my style sheet so I have a pared down version to hand. FYI: my style sheet started out as the Dirtylicious theme; I simply re-worked it to get the look and feel I wanted.

Powered By Logo

 

There definitely needs to be a single document explaining and justifying the architecture step-by-step. Why would you choose to go with a Web Site template for instance? Call me opinionated but you'd have to be pretty retarded to go for that particular option. I know, my bad ;-)

I've said it before: BlogEngine is a great piece of work: but, it's been over-engineered. Both users and developers have to jump through quite a few hoops to get it to work. The bells and whistles should be optional plug-and-play components. Perhaps the distribution model needs to be changed to something that would allow both developers and general users to download different configurations depending on their needs?

Captain's Log: One day later and both the host and myself are pulling out what's left of our hair. I have the blog working correctly on my local but failing every time I tried to deploy it, both normally and with FTP, via VS 2008. In the end, the only way I could deploy it was via FTP using a seperate FTP tool. The saga continues and I am seriously thinking of looking at a more basic and robust blog engine that I can work with. At this time of writing, the blog is really unstable and the referrer's page is crashing with an unhandled exception message: System.IndexOutOfRangeException: Cannot find table 0.

I'd really like to get the full source version working in my wwwroot folder and correctly configured in IIS7. Has anyone out there managed to do this with 1.4.5?

In the meantime, the issue has been reported so if someone has a solution/explanation, I will post it here :-)

kick it on DotNetKicks.com

Tags:

ASP.NET | Blog | IIS