One of the changes for ASP.NET 2.0 is that the class(es) for sending email messages have been enhanced. For starters, they're not in System.Web
, coz that's a silly place for them to be; instead, they're in System.Net
. I've also known, vaguely, that there have been additional enhancements to support functionality that was not previously available, such as embedding files (graphics), sending credentials, SSL support, et.. I thought I should look into this, because it won't be long before I'll be using 2.0 and sending emails (I assume the old classes still work, tho).
But I thought I'd start with just sending a simple email. And I thought it would be an interesting exercise to see if I could figure out how to do it without any documentation, just using the IDE -- specifically, IntelliSense and real-time compilation validation. (Since I'm a VB guy.) So here's how it went.
I knew that the message itself would be a class. So I started with Dim msg As New
and then let IntelliSense help me figure out that I wanted System.Net.Mail.MailMessage
. I admit this is a slightly unfair advantage over the total noob, in that I at least knew roughly what namespace to look in. Anyway, I started with:Dim msg As New System.Net.Mail.MailMessage()
To set the From address, I typed this:msg.From = "mike@elsewhere.com"[1]
Nope. The editor squiggles "From" and tells me "Value of type 'String' cannot be converted to System.Net.Mail.MailAddress." Aha. Addresses must be types now. I quick-like import System.Net.Mail
to save some effort. Then using IntelliSense I hunt around for a likely-sounding type for addresses and end up with this:Dim fromAddress As New MailAddress()
fromAddress.Address = "mike@elsewhere.com"
Error: Property 'Address' is Read-Only.
Hmm. Wonder why. Ok, I bet I can pass it in the constructor:Dim fromAddress As New MailAddress("mike@elsewhere.com")
That works. Cycling through the overloads, I learn that I can also set a display name:Dim fromAddress As New MailAddress("mike@elsewhere.com", "Mike")
I figure the To address must be similar, so I do this:Dim msg As New MailMessage()
Dim fromAddress As New MailAddress("mike@elsewhere.com", "Mike")
Dim toAddress As New MailAddress("mike@work.com", "Mike")
msg.From = fromAddress
msg.To = toAddress
Now msg.To
is squiggled: "Property 'To' is Read-Only."
Oh for heaven's sake. Why is From
settable but To
is not? I retrace and try overloads again, which yields this:Dim fromAddress As New MailAddress("mike@elsewhere.com", "Mike")
Dim toAddress As New MailAddress("mike@work.com", "Mike")
Dim msg As New MailMessage(fromAddress, toAddress)
Editor does not complain. On to the rest of the message! Using hints from IntelliSense, I construct this:msg.Body = "<b>Testing new email</b>"
msg.Subject = "Testing new email"
msg.IsBodyHtml = True
I guess that's enough actual message. I want to send my message now, and I notice the lack of two members I was expecting: a property to set the SMTP server name, and a send method. Those obviously belong to another class, so it's back to IntelliSense.
I peer through the classes in System.Net.Mail
and try a few. Ultimately I decide that SmtpClient
seems like the most promising, so I create this:Dim mailSender As New System.Net.Mail.SmtpClient()
The overloads allow me to specify host and port, which makes it seem like the right thing. I now have this:Dim mailSender As New System.Net.Mail.SmtpClient()
mailSender.Host = "(hostName)"
I look through the other members of the SmtpClient class. A property named DeliveryMethod
looks like it might be useful. IntelliSense offers me three enums for this property:SmtpDeliveryMethod.Network
SmtpDeliveryMethod.PickupDirectoryFromIis
SmtpDeliveryMethod.SpecifiedPickupDirectory
Arg. Man, I don't know. Our mail host is across the network, so that seems like a good choice. (Is it the default?) But perhaps it's an IIS-based SMTP virtual server; in that case, do I need to explictly specify a pickup directory? (Seems like that, too, would be defaulted.) Well, I'll just try sending it without setting the DeliveryMethod
property and see what happens. I'm pretty sure that the SmtpClient
class must have a Send
method. Whoa, it sure does -- it has three of them:Send
SendAsynch
SendAsynchCancel
I deduce (aren't I the brilliant one) that Send
is synchronous. I know that asynch methods generally require creating a callback delegate or something, and although it's possible I could do that, I don't need to right now. But it's duly noted for future reference.
Curiously, the SmtpClient
class has no obvious property for specifying which message to send. But once again IntelliSense rescues me and informs me that I can pass the message as an argument to the Send
method.
Since I have no idea if any of this is going to work, I figure I'll put the Send
method in a Try
block and dump whatever errors the Catch
block produces. Who knows, if it doesn't work, I might learn something. So now I have this:Try
mailSender.Send(msg)
Label1.Text = "Message sent."
Catch ex As Exception
Label1.Text = ex.Message
End Try
Shall we try it? Do let's. Compilation works, which I expected, since there were no squiggles. But I get this error, which makes me glad I used the Try/Catch
block:
Mailbox unavailable. The server response was 5.7.1. Unable to relay for (name).
I had used one of my non-work email addresses as the From address. Perhaps that's it? I change From and To to be the same (work) email address. Nope, same error.
Hmm. Hmm. A little googling to learn what the 5.7.1 error is, from which I deduce that the Exchange server is refusing to relay for anyone not in its domain. So I look again at the From and To addresses. Ok, well, one problem is that I misspelled the From address. A few more experiments, and I learn that our internal relay server will send only from and to addresses within our domain. (Or so it seems.)
Anyway, now it works. Here's the complete code as constructed entirely with IntelliSense and some help from the compiler squiggles:Dim fromAddress As New MailAddress("mike@work.com", "Mike @ work")
Dim toAddress As New MailAddress("mike@work.com", "Mike @ work")
Dim msg As New MailMessage(fromAddress, toAddress)
msg.Body = "<b>Testing new email</b>"
msg.Subject = "Testing new email, sent at " & DateTime.Now.ToString()
msg.IsBodyHtml = True
Dim mailSender As New System.Net.Mail.SmtpClient()
mailSender.Host = "(hostname)"
Try
mailSender.Send(msg)
Label1.Text = "Message sent."
Catch ex As Exception
Label1.Text = ex.Message
End Try