views:

117

answers:

4

Hi, I need to send lots of emails(probably hundreds a day) on schedule base. The way I'm thinking to do it is as follows but the problem is that my Body field can get very big, and if i add it as string it gets ugly.

        SmtpClient client = new SmtpClient(); //host and port picked from web.config
        client.EnableSsl = true;

        MailMessage message = new MailMessage();

        message.Body = "test from winservice"; // HERE IS MY PROBLEM
        message.IsBodyHtml = true;
        message.From = new MailAddress("[email protected]");
        message.Subject = "My subject";
        message.To.Add(new MailAddress("[email protected]"));
        try
        {
            client.Send(message);
        }
        catch (Exception)
        {

        }

When i had to do it from aspx page i used

    MailDefinition message = new MailDefinition();  

    message.BodyFileName = @"~\EmailTemplate\Template1.htm";
    ListDictionary replacements = new ListDictionary();
    replacements.Add("<% Name %>", this.txtName.Text);
    replacements.Add("<% PhoneOrEmail %>", this.txtPhoneOrEmail.Text);
    replacements.Add("<% Message %>", this.txtMessage.Text);
    MailMessage msgHtml = message.CreateMailMessage(RECIPIENTS, replacements, new LiteralControl());

I think it is elegant solution but i don't want to reference to System.Web.UI.WebControls.MailDefinition because i'm in winservice.

My questions are:

  1. What is the best way to send bulk emails from winservice?
  2. Is it posible to send it from gmail account? or they are going to block me after a while?

thanks for any help.

A: 

A simple answer is this:

message.Body = File.ReadAllText(@"~\EmailTemplate\Template1.htm");

I'm not sure about your specific points, but I'm sure someone else will answer those :)

Codesleuth
A: 

What's wrong with using

"messageBody".Replace("<% Name %>", curr.Name).Replace(....)...
?

As for sendign from gMail, you can do it through SMTP, but they do not let you spoof another "from" address, so the recipients will see it as coming from the gMail address.

gmagana
A: 

I would use some template engine. StringTemplate for instance.

empi
Wow, that website is hard on the eyes.
Ryan Emerle
+2  A: 

Why would you not use the exact same concept as the MailDefinition uses? Load the body from your template file, replace some markers with the text from another list - mail merge style?

All you're doing is a foreach over a data set of information to be merged with the template. Load your merge data, loop over the merge data replacing the tokens in your template with the current merge record. Set the message body as the currently built message. Attach the message to the message queue or send it now, whichever you choose.

It's not rocket science. You've got the code to create the message, so it's just a case of loading your merge data and looping through it. I've simplified to demonstrate the concept and I've used a CSV for the merge data and assumed that no field contains any commas:

message.IsBodyHtml = true;
message.From = new MailAddress("[email protected]");
message.Subject = "My bogus email subject";

string[] lines = File.ReadAllLines(@"~\MergeData.csv");
string originalTemplate = File.ReadAllText(@"~\Template.htm");

foreach(string line in lines)
{
    /* Split out the merge data */
    string[] mergeData = line.Split(',');

    /* Reset the template - to revert changes made in previous loop */
    string currentTemplate = originalTemplate;

    /* Replace the merge tokens with actual data */
    currentTemplate = currentTemplate.Replace("[[FullNameToken]]", mergeData[0]); 
    currentTemplate = currentTemplate.Replace("[[FirstNameToken]]", mergeData[1]);
    currentTemplate = currentTemplate.Replace("[[OtherToken]]", mergeData[2]);

    /*... other token replacements as necessary.
     * tokens can be specified as necessary using whatever syntax you choose
     * just make sure that there's something denoting the token so you can
     * easily replace it */

    /* Transfer the merged template to the message body */
    message.Body = currentTemplate;

    /* Clear out the address from the previous loop before adding the current one */
    message.To.Clear();
    message.To.Add(new MailAddress(mergeData[3]));
    client.Send(message);
}
BenAlabaster
I was thinking there is another way instead of loading htm as string and replacing tokens. But i guess there isn't.Thanks for your answer.
UshaP
@UshaP - There are many ways of loading the htm, but it doesn't always boil down to "what's the cleverest way of doing something". Usually the simplest approach is the best. You could (as someone else suggested) loading the htm as an XML document and using XPath to query and set the values. I'd say that's complete overkill and being too clever for being clever's sake for this particular situation.
BenAlabaster
Maybe it's a trivial question but i can't figure out how to take my htm path dynamically. I have a windows service that reference to BL Class Library project and there i put my htm file. File.ReadAllText i is looking in 'C:\Windows\system32\..
UshaP
Set the path in your app.config file that way you're not hard coding it to a pre-specified location. If it's in the local directory just use the file name i.e. "MyTemplate.htm", You can also use relative locations ".\Templates\MyTemplate.htm" or "..\Templates\MyTemplate.htm" or an absolute path such as "C:\MyTemplates\MyTemplate.htm". The reason you're getting C:\Windows\System32\... is because of the "~\" prefix I think. In an ASP.NET application that denotes the application root. IIRC in Windows Service apps, it references the System32 directory(?)
BenAlabaster
Thanks for your reply. This is what I ended up doing:I set the Copy to output directory to Copy if newer on my template folder.then string executablePath = Process.GetCurrentProcess().MainModule.FileName;string templatePath = string.Format("{0}\\EmailTemplate\\MatchupsReady.htm", Path.GetDirectoryName(executablePath));
UshaP