Click here to Skip to main content
15,885,546 members
Articles / Database Development / SQL Server

Ready-to-use Mass Emailing Functionality with C#, .NET 2.0, and Microsoft® SQL Server 2005 Service Broker

Rate me:
Please Sign up or sign in to vote.
4.84/5 (40 votes)
7 Sep 200613 min read 297.7K   4.6K   210  
This paper demonstrates an extensible mass emailing framework (Smart Mass Email SME). The demo implementation uses cutting edge .NET technologies available today such as C#, .NET 2.0, Microsoft® SQL Server 2005 Service Broker, MS Provider Pattern, Enterprise Library January 2006 etc.
#region Using Directives
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
#endregion

namespace SmartMassEmail.Data
{
	/// <summary>
	/// Provides the base functionality required to parse search terms.
	/// </summary>
	[CLSCompliant(true)]
	public abstract class ExpressionParserBase
	{
		#region Constructors

		/// <summary>
		/// Initializes a new instance of the ExpressionParserBase class.
		/// </summary>
		/// <param name="propertyName"></param>
		/// <param name="comparisonType"></param>
		/// <param name="ignoreCase"></param>
		protected ExpressionParserBase(String propertyName, SqlComparisonType comparisonType, bool ignoreCase)
		{
			PropertyName = propertyName;
			ComparisonType = comparisonType;
			IgnoreCase = ignoreCase;
		}

		#endregion Constructors

		#region Methods

		/// <summary>
		/// Appends the specified search text to the current expression.
		/// </summary>
		/// <param name="searchText">The search text to append.</param>
		protected void ParseCore(String searchText)
		{
			IList<String> quotedValues = new List<String>();
			
			int leftNumber = 0;
			int rightNumber = 0;
			int i = -1;
			
			// last param was a key word
			bool isKeyWord = false;

			// used to track if the first param is a key word
			// i.e., and or "john smith"
			int numParams = 0;

			// use AND to search all
			// i.e. John Smith would become John and Smith
			// however "John Smith" is one entity
			bool needToInsertAND = false;

			String outStr = ParseQuotes(searchText, quotedValues);
			StringTokenizer tokenizer = new StringTokenizer(outStr, "( ),\t\r\n", true);
			String nextToken;

			while ( tokenizer.HasMoreTokens )
			{
				// trim token
				nextToken = tokenizer.NextToken.Trim();

				// left parenthesis
				if ( nextToken.Equals(SqlUtil.LEFT) )
				{
					leftNumber++;

					if ( needToInsertAND )
					{
						AppendAnd();
					}

					OpenGrouping();

					needToInsertAND = false;
					isKeyWord = false;
				}

				// right parenthesis
				else if ( nextToken.Equals(SqlUtil.RIGHT) )
				{
					rightNumber++;

					CloseGrouping();

					needToInsertAND = true;
					isKeyWord = false;
				}
				// comma
				else if ( nextToken.Equals(SqlUtil.COMMA) )
				{
					AppendOr();
					needToInsertAND = false;
					isKeyWord = false;
				}
				// token is a key word (such as AND, OR,...)
				else if ( IsKeyWord(nextToken) )
				{
					numParams++;

					// if this is the first parameter in the entire string
					// treat it as a regular param (not a key word)
					if ( numParams == 1 )
					{
						needToInsertAND = true;

						AppendSearchText(nextToken);
					}

					// if only two params
					else if ( ( numParams == 2 ) && ( tokenizer.CountTokens <= 1 ) )
					{
						AppendAnd();
						AppendSearchText(nextToken);
					}

					// if the last string was a key word
					else if ( isKeyWord )
					{
						needToInsertAND = true;

						// treat this param as a regular string, not a key word
						isKeyWord = false;

						AppendSearchText(nextToken);
					}
					else
					{
						// make sure this is not the last param, if so use AND to search all
						if ( tokenizer.CountTokens <= 1 )
						{
							AppendAnd();
							AppendSearchText(nextToken);
						}
						else
						{
							if ( SqlUtil.AND.Equals(nextToken, StringComparison.OrdinalIgnoreCase) )
							{
								AppendAnd();
							}
							else if ( SqlUtil.OR.Equals(nextToken, StringComparison.OrdinalIgnoreCase) )
							{
								AppendOr();
							}

							needToInsertAND = false;
							isKeyWord = true;
						}
					}
				}
				else if ( nextToken.Equals(" ") )
				{
					AppendSpace();
				}
				else if ( nextToken.Equals("") )
				{
				}
				// string in quotes
				else if ( nextToken.Equals(SqlUtil.TOKEN) )
				{
					numParams++;

					if ( needToInsertAND )
					{
						AppendAnd();
					}

					needToInsertAND = true;

					// if the search param string is like: "John Smith" and Jones
					// the and needs to be translated to a SQL "AND"
					isKeyWord = false;

					// get the next quoted string
					i++;

					AppendSearchText(quotedValues[i]);
				}
				// a regular string other than the above cases
				else
				{
					numParams++;

					if ( needToInsertAND )
					{
						AppendAnd();
					}

					needToInsertAND = true;
					isKeyWord = false;

					AppendSearchText(nextToken);
				}
			}

			if ( leftNumber != rightNumber )
			{
				throw new ArgumentException("Syntax Error: mismatched parenthesis.");
			}
		}

		/// <summary>
		/// Parses quoted search terms.
		/// </summary>
		/// <param name="searchText"></param>
		/// <param name="quotedValues"></param>
		/// <returns></returns>
		private String ParseQuotes(String searchText, IList<String> quotedValues)
		{
			// sanity check
			if ( String.IsNullOrEmpty(searchText) || searchText.IndexOf('"') < 0 )
			{
				return searchText;
			}

			String[] tokens = searchText.Split('"');
			StringBuilder sb = new StringBuilder();
			bool needEndQuotes = true;

			foreach ( String token in tokens )
			{
				needEndQuotes = !needEndQuotes;

				if ( needEndQuotes )
				{
					sb.Append(SqlUtil.TOKEN);
					quotedValues.Add(token);
				}
				else
				{
					sb.Append(token);
				}
			}

			if ( needEndQuotes )
			{
				throw new ArgumentException("Syntax Error: mismatched quotes.");
			}

			return sb.ToString();
		}

		/// <summary>
		/// Determines whether the specified word is a reserved keyword.
		/// </summary>
		/// <param name="word"></param>
		/// <returns></returns>
		private bool IsKeyWord(String word)
		{
			return ( word != null && (
				SqlUtil.AND.Equals(word, StringComparison.OrdinalIgnoreCase) || 
				SqlUtil.OR.Equals(word, StringComparison.OrdinalIgnoreCase)
			));
		}

		#endregion Methods

		#region Abstract Methods

		/// <summary>
		/// Appends an OR expression.
		/// </summary>
		protected abstract void AppendOr();

		/// <summary>
		/// Appends an AND expression.
		/// </summary>
		protected abstract void AppendAnd();

		/// <summary>
		/// Appends an expression separator.
		/// </summary>
		protected abstract void AppendSpace();

		/// <summary>
		/// Appends a group opened expression.
		/// </summary>
		protected abstract void OpenGrouping();

		/// <summary>
		/// Appends a group closed expression.
		/// </summary>
		protected abstract void CloseGrouping();

		/// <summary>
		/// Appends the specified search text to the expression.
		/// </summary>
		/// <param name="searchText">The search text to append.</param>
		protected abstract void AppendSearchText(String searchText);

		#endregion Abstract Methods

		#region Properties

		/// <summary>
		/// The IgnoreCase member variable.
		/// </summary>
		private bool ignoreCase;

		/// <summary>
		/// Gets or sets the IgnoreCase property.
		/// </summary>
		public bool IgnoreCase
		{
			get { return ignoreCase; }
			set { ignoreCase = value; }
		}

		/// <summary>
		/// The PropertyName member variable.
		/// </summary>
		private String propertyName;

		/// <summary>
		/// Gets or sets the PropertyName property.
		/// </summary>
		public String PropertyName
		{
			get { return propertyName; }
			set { propertyName = value; }
		}

		/// <summary>
		/// The ComparisonType member variable.
		/// </summary>
		private SqlComparisonType comparisonType;

		/// <summary>
		/// Gets or sets the ComparisonType property.
		/// </summary>
		public SqlComparisonType ComparisonType
		{
			get { return comparisonType; }
			set { comparisonType = value; }
		}

		#endregion Properties
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Australia Australia
I have been awarded MVP (Visual C#) for year 2007, 2008, 2009. I am a Microsoft Certified Application Developer (C# .Net). I currently live in Melbourne, Australia. I am a co-founder and core developer of Pageflakes www.pageflakes.com and Founder of Simplexhub, a highly experienced software development company based in Melbourne Australia and Dhaka, Bangladesh. Simplexhub.
My BLOG http://www.geekswithblogs.net/shahed
http://msmvps.com/blogs/shahed/Default.aspx.

Comments and Discussions