In Part 1 of this series I talked about our experiences installing Community Server 2.0 on our production web host (ASP.NET 2.0, SQL Server 2000, Medium Trust environment).
In this part, I'll outline what we did to get emails working with our site. Before I start, keep in mind that Version 2.1 of Community Server is only weeks away, and it is highly likely that the new version will solve this issue once and for all. But in the interim, if you'd like to try a fairly straightforward workaround, read on...
Overview
The Problem
The site wasn't sending emails for new users, mass mailing, etc. The Email Job was failing with with an Iterator Failed message, there were security exceptions and "operation could destabilize the runtime" exceptions. An example message from the exceptions report:
Message Iterator Failed. Type CommunityServer.Components.EmailJob. Method SendQueuedEmailJob. Reason System.Security.Permissions.SecurityPermission
CommunityServer.Components.CSException: Iterator Failed. Type CommunityServer.Components.EmailJob. Method SendQueuedEmailJob. Reason System.Security.Permissions.SecurityPermission
The Options
-
Check that it is not related to the SMTP settings.
-
Look for a solution on the CS forums and the web.
- Rewrite the EmailQueueProvider and any dependencies to remove the problem.
- Replace the existing EmailQueueProvider with another compatible mechanism for sending emails.
The Assumptions
Solution
Option 1 looked the most likely so I put together a test ASP.NET page that sent an email back to me using hard-coded SMTP settings. I uploaded this to our host and ran it and it worked fine.
Option 2 was next so I grabbed the messages from the exceptions report and searched. It seems several other people were in the same boat with no solution in sight, and googling didn't help much either. Option 3 looked complicated and risked regression problems, plus with the 2.1 release just around the corner it wasn't worth the risks and effort.
So Option 4 was the answer. We took the code from our test ASP.NET page, adapted it to the CS Queue Provider model, and voila! the SendImmediatelyProvider was born. It does just what it says - as soon as an email is requested, it is sent. There is no queue. One assumption is particularly important here - you must only have light email traffic on your site to use this approach.
Here's what you need to do to use this class:
- build the code (see below, or download it from the attachment at the end of this post) - either within the SDK (Components project, Provider folder) or as a separate assembly (if separate, change the namespaces accordingly);
- remove the Emails job from communityserver.config (look for the <Jobs> element). (I first tried setting enabled=false but found that CS continued to execute the job even after recycling ASP.NET);
- change the EmailQueueProvider under the providers element again in communityserver.config. Change it to read as follows (make the appropriate adjustments if you have compiled a separate assembly)
<add name= "EmailQueueProvider" type= "CommunityServer.Components.SendImmediatelyProvider, CommunityServer.Components"
connectionStringName= "SiteSqlServer"databaseOwnerStringName= "SiteSqlServerOwner" />
And here's the SendImmediatelyProvider code:
1 namespace CommunityServer.Components {
2 using System;
3 using System.Collections;
4 using System.Net;
5 using System.Net.Mail;
6 using System.Web.Mail;
Note that Community Server 2.0 uses the .NET 1.1 System.Web.Mail namespace. This is deprecated in ASPNET 2.0 so our class uses the new System.Net.Mail namespace. System.Web.Mail is still required for compatibility with CS method calls.
7
8 public class SendImmediatelyProvider : EmailQueueProvider {
9
10 #region Member variables
11
12 protected string databaseOwner = "dbo";
13 private string connectionString = null;
14 private ProviderHelper sqlHelper = null;
15
16 #endregion
17
18 #region Constructor
19
20 public SendImmediatelyProvider(string databaseOwner, string connectionString) {
The next three lines are not strictly necessary for our provider but are included here for completeness of the provider pattern.
21 this.connectionString = connectionString;
22 this.databaseOwner = databaseOwner;
23 sqlHelper = ProviderHelper.Instance();
24 }
25
26 #endregion
27
28 public override void DeleteQueuedEmail(Guid emailID) {}
29
30 public override ArrayList DequeueEmail(int settingsID) {
31 return new ArrayList();
32 }
33
34 public override void QueueEmail(System.Web.Mail.MailMessage message) {
35 SiteSettings site = SiteSettingsManager.GetSiteSettings(1000);
1000 is the default community ID - if you are hosting multiple communities and they have different SMTP settings, you'll need to modify this code.
36 SmtpClient smtpClient;
Note the use of the new SmtpClient from the 2.0 .NET Framework.
37 if (String.IsNullOrEmpty(site.SmtpPortNumber))
38 smtpClient = new SmtpClient(site.SmtpServer);
39 else
40 smtpClient = new SmtpClient(site.SmtpServer, Convert.ToInt32(site.SmtpPortNumber));
41
42 smtpClient.UseDefaultCredentials = false;
43 if (site.SmtpServerRequiredLogin) {
44 NetworkCredential credential = new NetworkCredential(site.SmtpServerUserName, site.SmtpServerPassword);
45 smtpClient.Credentials = credential;
46 }
This next chunk of code simply migrates the values from the deprecated System.Web.Mail.MailMessage object to a new System.Net.Mail.MailMessage object in preparation for sending via SmtpClient.
47 System.Net.Mail.MailMessage netMessage = new System.Net.Mail.MailMessage(message.From, message.To);
48 netMessage.Subject = message.Subject;
49 netMessage.Body = message.Body;
50 netMessage.IsBodyHtml = (message.BodyFormat == MailFormat.Html);
51 netMessage.BodyEncoding = message.BodyEncoding;
52 netMessage.Priority = System.Net.Mail.MailPriority.Normal;
53
54 smtpClient.Send(netMessage);
55 }
56
57 public override void QueueSendingFailure(ArrayList list, int failureInterval, int maxNumberOfTries) {}
58 }
59 }
Technorati : ASP.NET 2.0, Community Server, Medium Trust