Pages

Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Thursday, November 4, 2010

C# Email Helper for sending out HTML Emails with data

Summary


The following article contains code to send data via a dictionary to a Mail Helper which builds an html email based on an Email template defined by a user control.

Premise


At work, I have had to build websites which send out html emails. We already had some code which loaded a user control into an html writer, which gave a html string to be placed in the html body for sending .Net mail. I wanted to abstract the logic out so that I could reuse it in any of my projects without having to modify all the parameters etc.

Essentially the things that were different were the HTML template, and the data to be bound to the template. Everything else; the building, the sending all had the same logic.

The Code


First we have the MailHelper class, which will build and send the email. We pass in the path of the email template, and basic email parameters such as to, from and subject. The dictionary is a string object pair for convenience. Use the string as a key to reference your objects similar to a ViewState or ViewModel.

Then you will see the EmailControl class which extends UserControl. We want to place a method there for setting the key value pairs. And then we want all Email templates to extend from this class.


public static class MailHelper
    {

        public static void SendEmail(string controlPath, 
                                     string to, 
                                     string from, 
                                     string subject, 
                                     Dictionary<string, object> keyValuePairs)
        {
            var body = GenerateContactEmailBody(controlPath, keyValuePairs);
 
            var officeMessage = new MailMessage(from, to)
            {
                Subject = subject,
                IsBodyHtml = true,
                Body = body
            };
 
            var client = new SmtpClient();
 
            client.Send(officeMessage);
        }
 
        private static string GenerateContactEmailBody(string path, 
                                  Dictionary<string, object> keyValuePairs)
        {
            Page pageHolder = new Page();
            var emailControl = (EmailControl)pageHolder.LoadControl(path);
            emailControl.SetKeyValuePairs(keyValuePairs);
            var writer = new StringWriter(CultureInfo.CurrentCulture);
            emailControl.RenderControl(new Html32TextWriter(writer));
            return writer.ToString();
        }
    }
 
    public class EmailControl : System.Web.UI.UserControl
    {
        public virtual void SetKeyValuePairs(Dictionary<string,object> value)
        {
            throw new NotImplementedException();
        }
    }

So now we can create a new HTML Email template. Create a new UserControl, make this extend EmailControl instead of extending UserControl directly. Then override the SetKeyValuePairs method. In my example below, I have a ContactUs email template where I accept values for Name, Email, Telephone, and Question. These are all strings but could have been other objects.

Then in my ascx file, I create the html template as per a design, and bind the properties where they are needed. The override for RenderControl is needed when we generate the html in the MailHelper.

public partial class ContactUs : EmailControl
    {
 
        public override void SetKeyValuePairs(Dictionary<string, object> value)
        {
            Name = value["Name"].ToString();
            Email = value["Email"].ToString();
            Telephone = value["Telephone"].ToString();
            Question = value["Question"].ToString();
        }
 
 
        public string Name { get; set; }
 
        public string Email { get; set; }
 
        public string Telephone { get; set; }
 
        public string Question { get; set; }
 
        public override void RenderControl(HtmlTextWriter writer)
        {
            DataBind();
            base.RenderControl(writer);
        }
    }

Finally, the bit of code which I call on my page which actually calls the MailHelper and sends out the email.

var kvp = new Dictionary<string, object>();
            kvp.Add("Name", name);
            kvp.Add("Email", email);
            kvp.Add("Telephone", telephone);
            kvp.Add("Question", question);
 
            var to = ConfigurationManager.AppSettings["ContactTo"];
            var from = ConfigurationManager.AppSettings["ContactFrom"];
            var subject = ConfigurationManager.AppSettings["ContactSubject"];
 
            MailHelper.SendEmail("~/ContactUs.ascx", to, from, subject, kvp);

Sunday, October 17, 2010

C# Random Password Generator

This blog post has moved to my new blog, you are about to be redirected...




In an earlier post, I wrote a Random Password Generator class intended for anyone interested to plug into their application.

Following some input I received, I have refactored the code a bit:

using System;
using System.Security.Cryptography;


namespace Security
{

    public class RandomPasswordGenerator
    {
        // Define default password length.
        private static int DEFAULT_PASSWORD_LENGTH = 8;

        //No characters that are confusing: i, I, l, L, o, O, 0, 1, u, v

        public static string PASSWORD_CHARS_ALPHA = 
                                "abcdefghjkmnpqrstwxyzABCDEFGHJKMNPQRSTWXYZ";
        public static string PASSWORD_CHARS_NUMERIC = "23456789";
        public static string PASSWORD_CHARS_SPECIAL = "*$-+?_&=!%{}/";
        public static string PASSWORD_CHARS_ALPHANUMERIC = 
                                PASSWORD_CHARS_ALPHA + PASSWORD_CHARS_NUMERIC;
        public static string PASSWORD_CHARS_ALL = 
                                PASSWORD_CHARS_ALPHANUMERIC + PASSWORD_CHARS_SPECIAL;
        
        //These overloads are only necesary in versions of .NET below 4.0
        #region Overloads

        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate()
        {
            return Generate(DEFAULT_PASSWORD_LENGTH,
                            PASSWORD_CHARS_ALL);
        }

        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate(string passwordChars)
        {
            return Generate(DEFAULT_PASSWORD_LENGTH, 
                            passwordChars);
        }

        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate(int passwordLength)
        {
            return Generate(passwordLength,
                            PASSWORD_CHARS_ALL);
        }

        /// 
        /// Generates a random password.
        /// 
        /// Randomly generated password.
        public static string Generate(int passwordLength,
                                      string passwordChars)
        {
            return GeneratePassword(passwordLength, 
                                    passwordChars);
        }

        #endregion


        /// 
        /// Generates the password.
        /// 
        /// 
        private static string GeneratePassword(int passwordLength,
                                               string passwordCharacters)
        {
            if (passwordLength < 0) 
                throw new ArgumentOutOfRangeException("Password Length");

            if (string.IsNullOrEmpty(passwordCharacters)) 
                throw new ArgumentOutOfRangeException("Password Characters");
            
            var password = new char[passwordLength];

            var random = GetRandom();

            for (int i = 0; i < passwordLength; i++)
                password[i] = passwordCharacters[
                                          random.Next(passwordCharacters.Length)];

            return new string(password);
        }



        

        /// 
        /// Gets a random object with a real random seed
        /// 
        /// 
        private static Random GetRandom()
        {
            // Use a 4-byte array to fill it with random bytes and convert it then
            // to an integer value.
            byte[] randomBytes = new byte[4];

            // Generate 4 random bytes.
            new RNGCryptoServiceProvider().GetBytes(randomBytes);

            // Convert 4 bytes into a 32-bit integer value.
            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];

            // Now, this is real randomization.
            return new Random(seed);
        }


    }
}


Thursday, September 30, 2010

What are TFS Labels for?

Just some interesting reading in the following links:

http://iworkonsoftware.blogspot.com/2010/04/tfs-labels.html

http://www.notionsolutions.com/notionmedia/articles/Pages/VirtuesandPitfallsoftheTFSLabel.aspx

How do you use TFS Labels?

Thursday, September 9, 2010

C# ASP.NET CheckBoxList selecting items on OnDataBinding doesn't work, set it on OnDataBound instead

When I try to bind some objects to a CheckBoxList in the OnDataBinding method, it never seems to select it when I tell it to do so:

protected override void OnDataBinding(EventArgs e)
 {
    base.OnDataBinding(e);
 
    chkBxLstProducts.DataSource = Product.GetAll();
    chkBxLstProducts.DataBind();
 
    foreach (var item in chkBxLstProducts.Items.Cast())
    {
         item.Selected = ((ICollection)FieldValue).Cast()
                         .Any(p => p.ProductId == Convert.ToInt32(item.Value));
    }
 }

 

When I move the selection code into the OnDataBound method, it works:

protected override void OnDataBinding(EventArgs e)
 {
    base.OnDataBinding(e);
    
    chkBxLstProducts.DataSource = Product.GetAll();
    chkBxLstProducts.DataBind();
 }

 protected void OnDataBound(object sender, EventArgs e)
 {
    foreach (var item in chkBxLstProducts.Items.Cast())
    {
        item.Selected = ((ICollection)FieldValue).Cast()
                        .Any(p => p.ProductId == Convert.ToInt32(item.Value));
    }
 }


I’m not 100% sure, but I think it’s because the OnDataBinding event occurs before the CheckBoxList is rendered. So after it’s rendered, everything is wiped clean and we lose the selection. Selecting items after OnDataBound ensures it gets rendered.

Tuesday, September 7, 2010

C# Random Password Generator

Below is a class for you to plug into your projects.

It generates random passwords by using actual random seed and omits those pesky letters that look similar to one another.

You can reuse the class by passing in options such as password length and the different characters you want to include. And since its the source code, you can extend/adapt it to your needs.

Enjoy

This blog post has moved to my new blog, you are about to be redirected...



using System;
using System.Security.Cryptography;
 
 
namespace Security
{
    public enum RandomPasswordOptions
    {
        Alpha = 1,
        Numeric = 2,
        AlphaNumeric = Alpha + Numeric,
        AlphaNumericSpecial = 4
    }
 
    public class RandomPasswordGenerator
    {
        // Define default password length.
        private static int DEFAULT_PASSWORD_LENGTH = 8;
 
        //No characters that are confusing: i, I, l, L, o, O, 0, 1, u, v
 
        private static string PASSWORD_CHARS_Alpha = 
                                   "abcdefghjkmnpqrstwxyzABCDEFGHJKMNPQRSTWXYZ";
        private static string PASSWORD_CHARS_NUMERIC = "23456789";
        private static string PASSWORD_CHARS_SPECIAL = "*$-+?_&=!%{}/";
 
        #region Overloads
 
        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate()
        {
            return Generate(DEFAULT_PASSWORD_LENGTH, 
                            RandomPasswordOptions.AlphaNumericSpecial);
        }
 
        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate(RandomPasswordOptions option)
        {
            return Generate(DEFAULT_PASSWORD_LENGTH, option);
        }
 
        /// 
        /// Generates a random password with the default length.
        /// 
        /// Randomly generated password.
        public static string Generate(int passwordLength)
        {
            return Generate(DEFAULT_PASSWORD_LENGTH, 
                            RandomPasswordOptions.AlphaNumericSpecial);
        }
 
        /// 
        /// Generates a random password.
        /// 
        /// Randomly generated password.
        public static string Generate(int passwordLength, 
                                      RandomPasswordOptions option)
        {
            return GeneratePassword(passwordLength, option);
        }
 
        #endregion
 
 
        /// 
        /// Generates the password.
        /// 
        /// 
        private static string GeneratePassword(int passwordLength, 
                                               RandomPasswordOptions option)
        {
            if (passwordLength < 0) return null;
 
            var passwordChars = GetCharacters(option);
 
            if (string.IsNullOrEmpty(passwordChars)) return null;
 
            var password = new char[passwordLength];
 
            var random = GetRandom();
 
            for (int i = 0; i < passwordLength; i++)
            {
                var index = random.Next(passwordChars.Length);
                var passwordChar = passwordChars[index];
 
                password[i] = passwordChar;
            }
 
            return new string(password);
        }
 
 
 
        /// 
        /// Gets the characters selected by the option
        /// 
        /// 
        private static string GetCharacters(RandomPasswordOptions option)
        {
            switch (option)
            {
                case RandomPasswordOptions.Alpha:
                    return PASSWORD_CHARS_Alpha;
                case RandomPasswordOptions.Numeric:
                    return PASSWORD_CHARS_NUMERIC;
                case RandomPasswordOptions.AlphaNumeric:
                    return PASSWORD_CHARS_Alpha + PASSWORD_CHARS_NUMERIC;
                case RandomPasswordOptions.AlphaNumericSpecial:
                    return PASSWORD_CHARS_Alpha + PASSWORD_CHARS_NUMERIC + 
                                 PASSWORD_CHARS_SPECIAL;
                default:
                    break;
            }
            return string.Empty;
        }
        
        /// 
        /// Gets a random object with a real random seed
        /// 
        /// 
        private static Random GetRandom()
        {
            // Use a 4-byte array to fill it with random bytes and convert it then
            // to an integer value.
            byte[] randomBytes = new byte[4];
 
            // Generate 4 random bytes.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            rng.GetBytes(randomBytes);
 
            // Convert 4 bytes into a 32-bit integer value.
            int seed = (randomBytes[0] & 0x7f) << 24 |
                        randomBytes[1] << 16 |
                        randomBytes[2] << 8 |
                        randomBytes[3];
 
            // Now, this is real randomization.
            return new Random(seed);
        }
 
 
    }
}

Monday, August 2, 2010

CSS Gradient that works cross browsers

Found this site very interesting.

http://www.webdesignerwall.com/tutorials/cross-browser-css-gradient/

Tuesday, July 13, 2010

.NET Deploy Multiple Assemblies (DLL) to the GAC

If you have too many assemblies you need to deploy to the GAC, and you can't drag and drop due to administrative rights, you can go through the Visual Studio Command Prompt.

Run Visual Studio Command Prompt in Administrator mode.

Change directory to the folder containing all the assemblies - its easy to just copy all the DLLs you need into some new folder and point it there.

Run the following:

FOR %1 IN (*) DO Gacutil /i %1

Saturday, May 1, 2010

Visual Studio Box Selection for Multi-Line Editing

Apparently this has been in Visual Studio 2008, but in 2010, this is greatly enhanced. Check out the video, this is awesome.

Monday, April 26, 2010

C# Improve Responsiveness in Real Time Searching

When you have real-time searching (searching as you type) you will inevitably get a performance hit because each letter you type will trigger a search which usually retrieves data from the database or at best a cache.

One solution is to add a delay and not search until the user has stopped searching for say 500ms.

//declare the timer
private Timer _searchQueueTimer;

...

//Initialize the timer in the constructor
_searchQueueTimer = new Timer();
_searchQueueTimer.Interval = 10;
_searchQueueTimer.Enabled = true;
_searchQueueTimer.Tick += new EventHandler(_searchQueueTimer_Tick);

...

/// 
/// Add the search string to a Queue
/// 
private string _currentSearchItem;
private string _queuedSearchItem;
private DateTime _queuedSearchTime;
public void Search(string search)
{
    if (search.Trim().Length > 0)
    {
        _queuedSearchTime = DateTime.Now;
        _currentSearchItem = search.Trim();
        _queuedSearchItem = _currentSearchItem;
    }
}

/// 
/// Delay the search while user is still typing 
/// before performing the actual search
/// 
private void _searchQueueTimer_Tick(object sender, EventArgs e)
{
    // Time in milliseconds to wait before initiating the search
    const int queueTime = 500;
    if (_queuedSearchItem != null 
     && DateTime.Now.Subtract(_queuedSearchTime).TotalMilliseconds >= queueTime)
        {
            Cursor = Cursors.WaitCursor;//Provide feedback to users
            DoSearch();
            Cursor = Cursors.Default;
        }
}

/// Some psudo code for the method which actually does the search
/// Removes the queued search text from the queue
private Results DoSearch()
{
      if (string.IsNullOrEmpty(_queuedSearchItem))
          return list;

      _queuedSearchItem = null;
      SearchDB();

      return results;
}

Monday, April 19, 2010

WPF Problem Loading Designer

If your designer view is having problem loading, its probably because you have some code-behind in the constructor of your xaml form or user control. Add the following;

if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
     // Code which causes the designer to fail
     // Stick this using segment inside the contructor 
     // code-behind and encapsulate all extra code
     // You may also need to add it to any initialize
     // or form load method if you have added extra code
}

Thursday, January 28, 2010

JQuery DataTables

I've recently been working on an MVC.NET project. Whats interesting is that most WebForms User controls don't work because there is no post back and code behind. (Don't quote me on this, I'm still very new to it).

So anyway, I needed a table that could Sort Columns, Search Filters, Paging... I conveniently found some JQuery Tables that work on HTML Tables.  See http://www.datatables.net/.

My solution built in MVC is to have a model of the data, and have the view output the model using HTML tables.  This is done in ASP.NET so you use angle brackets inside the asp code to loop through all the entries.  Then you use the above jquery library, which is completely client side, no server side calls.

The downside is, you retrieve all your data the first go, and load the entire table, which is slower than real paging.
Reblog this post [with Zemanta]

Friday, January 15, 2010

C# Reading from a closed Stream

Basicaly, when you read from a Stream, you must close it after you are done.  But what if at some point later you need to open it and read it again?  Even after trying to reopen it, it seems like the pointer is at the end and it just finishes :S

We copy the stream we want to read first, then read that copied stream.  I found this code on the net somewhere, can't remember now.  Very handy, stored here for later reference :)

*edit* updated by adding one line to the CopyStream method

...
//Copy the stream first so we can open it several times
Stream responseStream = CopyStream(attachment.ContentStream);

// convert stream to string
using (StreamReader reader = new StreamReader(responseStream))
{
    string text = reader.ReadToEnd();
    return text;
}
...

///
/// Copy the stream for reading and writing
///
///
///
private static Stream CopyStream(Stream inputStream)
{
    const int readSize = 256;
    byte[] buffer = new byte[readSize];
    MemoryStream ms = new MemoryStream();

    int count = inputStream.Read(buffer, 0, readSize);
    while (count > 0)
    {
        ms.Write(buffer, 0, count);
        count = inputStream.Read(buffer, 0, readSize);
    }
    ms.Seek(0, SeekOrigin.Begin);
    inputStream.Seek(0, SeekOrigin.Begin); 
    return ms;
}

Saturday, November 21, 2009

Visual Studio Post Build Event to Copy DLLs into one Directory

*Updated 30th March 2010*

Basically I have this problem: I have a big solution with several projects.  Each  project builds into a dll which gets placed in its Bin directory at the project level.  All the necesary dlls are also included in the bin directory.  However, I want to be able to colllect all dlls for the solution into one folder for me to easily deploy onto the live server.

Put this into the Post Build Event in each of your projects:

xcopy /y $(ProjectDir)$(OutDir)$(TargetFileName) $(SolutionDir)GAC\

This will take the all output files from the current project and copy it to the solution root and into the folder called GAC.  Doing this to each of the projects will give you the collection of dlls in that GAC folder.

If you want to only do this on Release Build add:


if $(ConfigurationName) == Release xcopy ...

Tuesday, September 8, 2009

If Architects Had to Work Like Web Designers

An article I found here... http://www.digitalsurvivors.com/archives/000455.php

January 10, 2002

Please design and build me a house. I am not quite sure of what I need, so you should use your discretion. My house should have somewhere between two and forty-five bedrooms. Just make sure the plans are such that the bedrooms can be easily added or deleted. When you bring the blueprints to me, I will make the final decision of what I want. Also, bring me the cost breakdown for each configuration so that I can arbitrarily pick one.

Keep in mind that the house I ultimately choose must cost less than the one I am currently living in. Make sure, however, that you correct all the deficiencies that exist in my current house (the floor of my kitchen vibrates when I walk across it, and the walls don't have nearly enough insulation in them).

As you design, also keep in mind that I want to keep yearly maintenance costs as low as possible. This should mean the incorporation of extra-cost features like aluminum, vinyl, or composite siding. (If you choose not to specify aluminum, be prepared to explain your decision in detail.)

Please take care that modern design practices and the latest materials are used in construction of the house, as I want it to be a showplace for the most up-to-date ideas and methods. Be alerted, however, that kitchen should be designed to accommodate, among other things, my 1952 Gibson refrigerator.

To insure that you are building the correct house for our entire family, make certain that you contact each of our children, and also our in-laws. My mother-in-law will have very strong feelings about how the house should be designed, since she visits us at least once a year.

Make sure that you weigh all of these options carefully and come to the right decision. I, however, retain the right to overrule any choices that you make.

Please don't bother me with small details right now. Your job is to develop the overall plans for the house: Get the big picture. At this time, for example, it is not appropriate to be choosing the color of the carpet. However, keep in mind that my wife likes blue.

Also, do not worry at this time about acquiring the resources to build the house itself. Your first priority is to develop detailed plans and specifications. Once I approve these plans, however, I would expect the house to be under roof within 48 hours.

While you are designing this house specifically for me, keep in mind that sooner or later I will have to sell it to someone else. It therefore should have appeal to a wide variety of potential buyers.

Please make sure before you finalize the plans that there is a consensus of the population in my area that they like the features this house has. I advise you to run up and look at my neighbor's house that he constructed last year. We like it a great deal. It has many features that we would also like in our new home, particularly the 75-foot swimming pool. With careful engineering, I believe that you can design this into our new house without impacting the final cost.

Please prepare a complete set of blueprints. It is not necessary at this time to do the real design, since they will be used only for construction bids. Be advised, however, that you will be held accountable for any increase of construction costs as a result of later design changes.

You must be thrilled to be working on as an interesting project as this! To be able to use the latest techniques and materials and to be given such freedom in your designs is something that can't happen very often.

Contact me as soon as possible with your complete ideas and plans.

PS: My wife has just told me that she disagrees with many of the instructions I've given you in this letter. As architect, it is your responsibility to resolve these differences. I have tried in the past and have been unable to accomplish this. If you can't handle this responsibility, I will have to find another architect.

PPS: Perhaps what I need is not a house at all, but a travel trailer. Please advise me as soon as possible if this is the case.