Solution Explorer Recently I had to send an email blast out to about two thousand email subscribers from a client's ASP.NET Web Forms application. The client had an admin form where they would compose and send out the email. Unfortunately, with that many emails the Web form would inevitably hang. Plus, with Exchange Server there is no easy way to know how many emails actually got sent; you could use Powershell to determine this or you could log it but it still isn't a robust way of doing things.

The conventional wisdom is to use a third party service for mass mailings and most times this is probably your best option. In this situation, an email blast is only sent every 5 years so it really didn't make sense to enrol in a monthly plan. With a little due diligence, this can be accomplished with a very simple console application which can be called from the Web form to dispatch the emails.

The basic design is that the subject line and email body text are entered in the form and passed to the console application as string parameters. In the console app, we can obtain the email addresses from a database and send the emails out in batches with a pause between each batch.

Some basics first: the entry point to any console application is the Main method:

// Mailer class in console application

static void Main(string[] args)
{
    if (args.Length >= 2)
    {
        SmtpClient smtpClient = new SmtpClient(EMAIL_HOST);
        smtpClient.Host = "localhost";

        MailMessage message = new MailMessage();
        message.Priority = MailPriority.High;
        message.Subject = args[0];
        message.Body = args[1];
        smtpClient.Send(FROM_ADDRESS, TO_ADDRESS, message.Subject, message.Body);
    }
}

The array of strings args parameter corresponds directly to the command line parameters passed to the console application; string[0] is the first parameter, strg[1] is the second parameter and so on. This is different in C and C++, where the name of the console application itself is the first parameter... when in doubt, try it out!

Note that when testing this on the command line initially, that there is a limitation on the size of the strings you can pass. This will not apply when sending the parameters programmatically from the Web form code-behind. You can test that your console app is sending emails by creating a drop folder on your C drive. Download and install Live Mail to view the .eml files generated. You can create a dummy gmail account to get this running and choose not to make Live Mail your default email client during the install (if this is your preference). This is a really simple and useful way to test email functionality locally on your dev machine. Just update your Web.Config file with the following to get it working:

<system.net>
    <mailSettings>
        <smtp deliveryMethod="SpecifiedPickupDirectory">
            <specifiedPickupDirectory pickupDirectoryLocation="c:\maildrop" />
        </smtp>
    </mailSettings>
</system.net>

To generate a test email with the console app and pass the subject and body parameters, call it like this:

// DOS command line

C:\>ConsoleApplication1 "Subject Line Text" "Some body text"

To call the console app from the Web form code-behind we use the Process.Start method. We can avail of the ProcessStartInfo class to set up any properties in advance of making the process call.

// Web form code-behind
// Pass subject and message strings as params to console app

ProcessStartInfo info = new ProcessStartInfo();

string arguments = String.Format(@"""{0}"" ""{1}""",
     subjectText.Text.Replace(@"""", @""""""),
     messageText.Text.Replace(@"""", @""""""));
info.FileName = MAILER_FILEPATH;
info.Arguments = arguments;
Process.Start(info);

We also need to take account of the possibility of quotes being included in the subject line or body text. I enlisted some help on this issue with a StackOverflow question. The solution was a combination of composite string formatting and verbatim "@" string literals. See the String.Format call in the code snippet above.

 

Email Form

If you've been testing the console app and then go on to debugging with VS, you may come up against a file locking issue. This issue is a known bug - I've seen bug reports going back to 2006 on this one.

Error 9 Unable to copy file "obj\x86\Debug\ConsoleApplication1.exe" to "bin \Debug\ConsoleApplication1.exe". The process cannot access the file 'bin\Debug\ConsoleApplication1.exe' because it is being used by another process.

 

Processes

There are two workarounds to this. My way is to close out VS and then kill any remaining processes for the console app: Windows Task Manager -> Processes tab -> Show processes from all users -> right- click each process and kill it (End Process). The second method is to add a pre-build script.

Tags: , ,

ASP.NET | C#



ASP.NET Contact Forms

by agrace 30. November 2008 07:52

ASP.NET Contact Forms I've seen some blog postings out there recently on what to do if your contact form fails for some reason. Some have suggested including your email address using JavaScript, which I do not recommend. Some people even suggest substituting "@" with "at"; if I was writing a screenscraper, this is the first thing I'd check for, after "dotcom"! If you're going to try and include your email address, then why bother with a contact form in the first place?

I've been using my own standard contact form for some time now. It displays either a success or error message upon submission. If there is an error, then it generates an email back to me. In fact, I like to have an email generated whenever there is an application error in a client's website, and I include this as part of routine error handling in my projects. This works for small projects where it is unlikely to over-burden the mail server. The strategy pattern helps me to factor out this kind of repetitive functionality; see an earlier post about this. At the end of this post, you can find a link to download a simple contact form.

ASP.NET Contact Forms

 

A lot of people have problems testing email with ASP.NET. The simple answer is to use a drop folder on your local machine when developing. Just create a folder called "maildrop" on your c:/ drive and use the following in your Web.config file:

<mailSettings>
    <smtp deliveryMethod='SpecifiedPickupDirectory'>
        <specifiedPickupDirectory pickupDirectoryLocation="c:\maildrop" />
    </smtp>
 </mailSettings>

 

ASP.NET Contact Forms

 

When you are ready to deploy your application, simply comment out the above entry in the config file and use the usual syntax below. Keep the drop directory section commented out in your config file as you might need it later for testing or further development:

<mailSettings>
    <smtp from="info@customersite.com">
        <network host="smtp.somehost.net"/>
    </smtp>
</mailSettings>

 

For the CSS purists out there, this is created using tables because we are more interested in the functionality here. If you want a CSS contact form layout, just holler in the comments :-)

Depending on the importance of the project you are working on, you may want to log the errors to a database. This is simple to do so I'm not going to delve into it here. Note, that there is nothing to prevent you from grabbing some fine-grain error details and storing them in your errors table also. Anything that helps you identify problems at a later date is a good candidate for a table field.

That just leaves network errors. I haven't had a chance to play with this yet, but it would be nice to know in advance if the mail server was down and I'm wondering if we could somehow tap into the SMTP return status codes before sending the mail? If anyone has any suggestions along these lines, please share them here?

ContactForm.zip (22.61 kb)



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