abSee ‘ay be see’ – A/B Test library

I have often wondered “What if” when it comes design aspects of a website.

  • What if this button was green instead of blue?
  • What if I call it an upgrade instead of a purchase?
  • What if the background wasn’t as dark?
  • What if I make that notification flashing red instead of dark yellow?

You get the point…

So I’ve been working on an A/B testing library with the 1 goal of making it simple to insert tests into my web applications, thus abSee was born!

abSee is in NuGet so its pretty easy to install. (hurray!)

Its usage is simple enough also:

Firstly fire up an instance of the Tester, do so by calling the Start function. Or you could filter out certain users here to only show tests for them. (non-Admins etc.)

protected void Application_BeginRequest()
{
    ABTester.Start();
}

From there we can fire off tests to the User where ever we like by calling the Test function. Now to make a test we need to give it a name, this will be used to identify what we are testing in this specific instance. Then you give the function the different options and it will randomly select 1 for you as the output and save the result.

var output = ABTester.Test("MyTest1", "A", "B");

After running the test we then need to let the Tester know if it was successful or not so we can track conversions of the different options. We do this by calling the Convert function with the test name from the test we ran before. abSee will then store that result for us as converted.

ABTester.Convert("MyTest1");

So now we are running tests, how do I see the results?

You can get results for a given test using the GetResults function, this function returns a list of ABSeeResult which contains a few details about the test instance as well as the option that was selected and if it was converted or not.

var results = abSee.ABTester.GetResults("MyTest1");

 

More detailed documentation is coming soon with details on implementing custom storage and user identification.

Feel free to leave me some feedback about abSee or even fork it on Github.

PurpleSmoke: Ecommerce for SkyPoint

So in my day job I work for a company called Ardent Leisure, and lately I have been working on a massive project to build an integrated ecommerce engine for SkyPoint, Dreamworld and WhiteWater World. I thought I might take some time to talk about the technologies used here, being a .NET developer I kept pretty close to the Microsoft Stack, with a few open source libraries in the mix.

framework Microsoft ASP.NET
language C# (.NET v4.0)
development environment Visual Studio 2010
web frameworks ASP.NET MVC3
client side framework JQuery (+ UI and tmpl)
Knockout
web service layer Windows Communication Framework
database SQL Server 2008 R2
data access layer LINQ to SQL
source control Team Foundation Server 2010

 

3rd Party Tools:

 

Winning Combinations:

For me their are a few winning combinations here:

1. RestSharp + Newtonsoft JSON.NET + WCF.

Now my love for RestSharp has been widely documented but I cant say it enough, this is an awesome library and using it with a WCF web service was great. However this combination is not without its flaws, mianly DateTimes in WCF. But I was able to get around this “Feature” by creating my own WCFDate Class which serialises to and from a formatted string. This was quite a hack but its working pretty well for us at the moment.

2. Knockout + Jquery + tmpl.

This was the first project that I have actually used Knockout for and it did take some time to get up to speed on how it all works together but now I couldn’t imagine doing this project without it. It gave us the flexibility we needed to display the data we needed where ever we wanted.

 

You can find PurpleSmoke lurking among the CMS Pages at www.skypoint.com.au

From TFS to Private NuGet

Let me start by saying this: I love NuGet, its straight up awesome!

This post is about how I created a TFS CI Build to create NuGet packages and push them to a private NuGet server hosted on site.

I have been playing around with the TFS Build server, mainly for a Continuous Integration (CI) build on some of our projects and I wanted a way for me to be able to update a core library and have it propagated to all of the projects that use that library without them all needing to be in the same solution.

The answer was a private NuGet server with a CI build that automatically packaged my library.

I started by setting up a private NuGet server, reading Phil Haack’s post about hosting a simple package feed. Once that was done I could start pumping out packages!

Now that I have my NuGet server I need to setup the build. I created an Ardent.CommonBuild.proj for my build config and tasks. I found its easier to manage as a separate file to the actual project file, this file has to be a .sln or .*proj file for the TFS Build server to be able to run it.

image

Now I also wanted to control the version numbers of the packages being pushed out (both manually and automatic). So I set a major and minor version property at the top.

<PropertyGroup>
    <VersionMajor>0</VersionMajor>
    <VersionMinor>1</VersionMinor>
</PropertyGroup>

I then wanted to include the TFS Changeset number as the 3rd part of the version. So I kicked off a TfsVersion task which outputs the changeset number of a specific path (Set this to the project root to get all changes).

<TfsVersion
  TfsLibraryLocation="C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0"
  LocalPath="$(MSBuildProjectDirectory)">
  <Output TaskParameter="Changeset" PropertyName="ChangesetNumber"/>
</TfsVersion>

From there I build the Project, normally the TFS build outputs the dlls to a binaries folder in the build output folder, but I decided to skip the nuspec file step but pointing NuGet at the project file directly. However you could create a nuspec file and point that to the built dlls, your choice. I also set the version number here for the build.

<MSBuild Projects="$(MSBuildProjectDirectory)\Ardent.Common\Ardent.Common.csproj"
    Targets="Build"
    Properties="Configuration=Debug;
      OutDir=$(MSBuildProjectDirectory)\Ardent.Common\Bin\Debug\;
    ApplicationVersion=$(VersionMajor).$(VersionMinor).$(ChangesetNumber)" />

So now we have built our project and we have the version we want so lets create the package. Using the Exec function I then run the NuGet.exe pack command on my project file using the –Version flag to set the version of the package. I also cheated a bit here by setting the –OutputDirectory flag and pointing that to the packages folder on the NuGet server I created. This will populate the feed now with the new package.

<Exec Command="C:\NuGet\NuGet.exe pack $(MSBuildProjectDirectory)\Ardent.Common\Ardent.Common.csproj -Version $(VersionMajor).$(VersionMinor).$(ChangesetNumber) -OutputDirectory \\grp-dev02\f$\Websites\Ardent.NuGet\Packages"></Exec>

The only thing left to do is create a TFS build definition under that points to my Ardent.CommonBuild.proj file in source control set the definition to CI mode. Done!

image

Now for every check-in to that project in TFS I get a new package created with the updated code then NuGet handles the updates in all of my other projects.

image

Life is good…

DateTime + WCF: How I tamed the beast

I have been working a bit with WCF lately and like everyone else use Json with WCF I ran into some issues with DateTime formats. I kept trying to find a valid solution to this where it outputted a readable value.

Eventually I started outputting the DateTime as a formatted string and this worked, but it meant I have to create new string variables in all of my classes instead of the DateTimes that I already had and was using, but then I still needed the variable in its DateTime form.

So I came up with the WCFDate class, at its heart this class outputs a string in a set format, this allows it to be easily read by what ever needs to. I could then replace all of my DateTimes with WCFDate and all was good in the world.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

/// <summary>
/// Class to help get around the craziness of WCF DateTime formats (Use this class instead of DateTime for WebService Models)
/// </summary>
public class WCFDate
{

    public static string DateTimeFormat = "yyyy-MM-dd hh:mm:ss zz";

    /// <summary>
    /// The Date as a formatted string
    /// </summary>
    public string Data { get; set; }

    /// <summary>
    /// Basic constructor (with no value)
    /// </summary>
    public WCFDate() { }
    /// <summary>
    /// String constructor setting a Formatted string for the DateTime
    /// </summary>
    /// <param name="data"></param>
    public WCFDate(string data)
    {
        Data = data;
    }
    /// <summary>
    /// DateTime constructor, sets the Data to the formatted String from the DateTime
    /// </summary>
    /// <param name="date"></param>
    public WCFDate(DateTime date)
    {
        Data = date.ToString(DateTimeFormat);
    }

    /// <summary>
    /// DateTime constructor, sets the Data to the formatted String from the DateTime
    /// </summary>
    /// <param name="date"></param>
    public WCFDate(DateTime? date)
    {
        if (date.HasValue)
        {
            Data = date.Value.ToString(DateTimeFormat);
        }
    }

    /// <summary>
    /// Check if the class is holding a Date or not
    /// </summary>
    public bool HasDate
    {
        get
        {
            return !string.IsNullOrWhiteSpace(Data);
        }
    }

    /// <summary>
    /// Gets the DateTime the class represents
    /// </summary>
    /// <returns></returns>
    public DateTime GetDate()
    {
        try
        {
            return DateTime.ParseExact(Data, DateTimeFormat, CultureInfo.CurrentCulture);
        }
        catch
        {
            return new DateTime();
        }
    }
}

Follow the Gist of this class or feel free to add to it.

AppEvents – Do stuff when things happen (WP7)

Ever wanted an easy way for apps to track certain events then run code based off what events the user has triggered? Well now you can with AppEvents!

So AppEvents is a Windows Phone 7 library to manage events for a user across multiple sessions. In this example I will use it with CodeTrack to prompt the user to rate the app after they have added 3 repos.

Lets get started, App.xaml.cs (this code can be put anywhere in the app but I felt this was better to be done at the start of the app)

public App()
{
    ...
    EventClient.New()
        .Add(RuleSet.When("rate-app",
            el => el.Any(e => e.Name == "addrepo" && e.Occurrrences.Count > 2))
            .Do(r => AskToRate())
        );
}

Now lets break that down, first we have the New() function being called, this creates the instance (and sets the Current singleton in the EventClient class). It also uses the default JsonEventStorageProvider to store the EventStore data, this can be overwritten with a type parameter on the new function:

EventClient.New<MyCustomEventStoreage>()

Now this is where the RuleSets are added to the client using RuleSet.When() The first parameter of this function is a name of the rule. This is mainly to keep track of the rule and make sure we don’t run it twice. The second parameter is a Func<UserEventList, bool> which means we can create lamba expressions to be run agains the UserEventList object, this expression tells the RuleSet when it should fire its Do Action.

    .Add(RuleSet.When("rate-app",
        el => el.Any(e => e.Name == "addrepo" && e.Occurrrences.Count > 2))
        .Do(r => AskToRate())
    );

So where do I get it?

The last part to this is firing off the events this part is very simple.

    EventClient.Current.Fire("addrepo");

And thats pretty much it, the library then does the rest

The library also allows for manual checking of events if for instance you fire an event on a page but want the RuleSet to be executed on a different page. To do this we simply add a false optional parameter to the Fire method to stop it running the RuleSets with the event. We can also manually run rules, either singly by the RuleSet name or all the rules in the list. We can also set the EventClient to not save the EventStore using the provider each time (and just have it saved later for performance).

EventClient.Current.Fire("addrepo", false);
EventClient.Current.Fire("addrepo", false, false);
EventClient.Current.RunRule("rate-app");
EventClient.Current.RunRules();
EventClient.Current.SaveEventStore();

So where do I get it?

The project if on GitHub for your forking, issues and discussions.AppEvents on Github.

Windows Mobile 6: Legacy

I have also made a Windows Mobile 6 version of this library but haven’t actively tested it too much, if you feel like giving it a go be sure to let me know how you go.

Dropbox on Windows Phone 7 with DropNet

So I have been working on an open source Dropbox client library for a while now called DropNet. I have recently put this project into NuGet so its super easy to add Dropbox support to your Windows Phone 7 apps.

Update: The source for this sample project is available here: http://bit.ly/dYjRnW

Update: The DropNet and DropNet-WP NuGet packages have been merged and allow for both Project Types.

I’ll start with a File -> New Project, creating a sample Windows Phone Application.

FileNew2

First thing to do is install the DropNet package, NuGet will automatically detect the type of project you are using and add the DropNet reference for that project type (eg. DropNet.WindowsPhone for Windows Phone 7 projects.)

DropNetNuGet

Ok, now we can start with the App, for starters I created a global variable for my DropNetClient instance.

GlobalClient = new DropNetClient("API KEY", "API SECRET");

Now from that we will need to let the user login so my main page has a basic login form, 2 textboxes for Email and Password and a button to make it work.

private void Login_Click(object sender, RoutedEventArgs e)
{
    //try login
    App.GlobalClient.LoginAsync(_model.Email, _model.Password,
        (response) =>
        {
            if (response.Data != null)
            {
                //DropNet automatically sets the result token inside the client
                NavigationService.Navigate(new Uri("/FilesPage.xaml", UriKind.Relative));
            }
        });
}

DropNets Login function automatically saves the user Token and Secret to its client instance so we dont have to set it. Then I just navigate to a new page where we will attempt to browse our Dropbox Folder.

This page has a basic view model with 1 property for the folders content.

private ObservableCollection<MetaData> _contents;
public ObservableCollection<MetaData> Contents
{
    get { return _contents; }
    set
    {
        _contents = value;
        NotifyPropertyChanged("Contents");
    }
}

Pretty easy, the page itself simply binds this collection to a listbox displaying the Name and an icon if its a folder.

Ok, now to actually get the data from Dropbox… We do this by calling our DropNetClient’s GetMetaDataAsync method. This method takes a path parameter so we can tell it which folder to get the metadata for.

App.GlobalClient.GetMetaDataAsync("/",
    (response) =>
    {
        foreach (var c in response.Data.Contents.OrderBy(f => f.Name).OrderByDescending(f => f.Is_Dir))
        {
            _model.Contents.Add(c);
        }
    });

Browse1

After getting the MetaData I then order it, folders first then alphabetically.

Wait, really? Thats it?

Yep! Thats my Dropbox folder.

Now to make it even better we can handle the listbox’s selection change event and if the selected item was a folder we can show its contents.

private void ListBox_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    if (e.AddedItems.Count > 0)
    {
        var selected = (e.AddedItems[0] as MetaData);

        //if its a folder browse to it
        if (selected.Is_Dir)
        {
            _model.Contents = new ObservableCollection<MetaData>();
            App.GlobalClient.GetMetaDataAsync(selected.Path,
            (response) =>
            {
                foreach (var c in response.Data.Contents.OrderBy(f => f.Name).OrderByDescending(f => f.Is_Dir))
                {
                    _model.Contents.Add(c);
                }
            });
        }
    }
}

 

There we go, thats the basis for a Dropbox app using DropNet, lets see what we can come up with!

DropNet Implements most of the API’s functions, including :

  • Uploading files
  • Downloading files
  • Creating folders
  • Creating an account
  • Getting account info
  • Deleting files/folders
  • Data usage counter

I’m always updating the project over at GitHub, drop by and let me know what you think or if you have any feature requests/bugs. GitHub Project.

Or check out the NuGet Package here.

Browse3Browse2

BitBucket API from Windows Phone 7 and C#

With my latest update of CodeTrack (1.2) I implemented BitBucket as a provider, so you can watch repositories from there now as well. On the app itself there are a few search functions using the BitBucket API, this post will provide some insight and example code for doing this.

So basically I wanted to go from having nothing at all (no BitBucket user, no username, no repo slug/id) to being able to search for repositories by either their name or their owners username.

Note: The BitBucket API has recently changed to allow you to do this by giving the repository responses the Onwer property.

As always, RestSharp was used in the making of this code sample.

The 2 requests share the same response class BitBucketRepoList, this class is pretty simple because we are calling the API anonymously where using a BitBucket username/password would return a more detailed response.

public class BitBucketRepoList
{
    public List<BitBucketRepo> Repositories { get; set; }
}

public class BitBucketRepo
{
    public string Website { get; set; }
    public string Slug { get; set; }
    public string Name { get; set; }
    public string Owner { get; set; }
    public int FollowersCount { get; set; }
    public string Description { get; set; }
}

Searching by Repo Name

This request is pretty straight forward, RestClient with the base url for the API, RestRequest with the Resource “repositories” then add a get parameter of "name” with the search term. (API Doco)

_client = new RestClient("https://api.bitbucket.org/1.0");
var request = new RestRequest("repositories/");

request.AddParameter("name", searchTerm);

_client.ExecuteAsync<BitBucketRepoList>(request, (response) =>
{
    //Do stuff here with response
});

Searching by Repo Owner

Now for this request, BitBucket API doesnt actually support searching on usernames so these must be exact matches on the username.

_client = new RestClient("https://api.bitbucket.org/1.0");
var request = new RestRequest("users/{username}");

request.AddParameter("username", username, ParameterType.UrlSegment);

_client.ExecuteAsync<BitBucketRepoList>(request, (response) =>
{
    //Do stuff here with response
});

 

And there you have it, BitBucket API from Windows Phone 7. (The above code will also work in Silverlight/WPF as well as Win forms or ASP.NET depending on the version of RestSharp used.)

Easy Email Templates in .NET with FluentEmail

Update: The “Replace” method of templating in Fluent Email has itself been replaced by a Razor Engine Template. See Luke’s Post here about it.

I always seem to forget how to send emails in .NET which to me seems like such a simple task. Lately I’ve been working on updating an older application of mine that’s soul purpose was to read a database and send emails depending on certain events. I had a look at the current code and saw something like this:

sb.Append("A Transaction has been made...<br />");
sb.Append("<table><tr><th>Reservation Number</th><th>Customer Name</th><th>Email</th><th>Sale Date</th><th>Amount</th></tr>");
sb.AppendFormat("<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>",
    transaction.Reservation, transaction.FullName, transaction.Email, transaction.SaleDate, transaction.Amount.ToString("C2"));
sb.AppendFormat("<tr><th>Invoice</th><th>Confirmation</th></tr><tr><td>{0}</td><td>{1}</td></tr></table>", transaction.Invoice, transaction.Confirmation);

Terrible! So I figured while I was updating it I’d work out a better way build up the HTML of the emails. I started with the FluentEmail Project (Luke’s Post about it). From there I added some basic templating features, such as loading a HTML file with placeholders for content and the rest is history…

 

With my recent updated to FluentEmail I was about to turn something that looked like the above code to this: (as always C# is the code of choice)

//Send Email!
var email = Email
            .From(_fromEmail)
            .To(toAddresses)
            .Subject(string.Format("Siriusware Emailer Confirmation #{0}", transaction.Confirmation))
            .UsingTemplate(@"C:\Emailer\TransactionTemplate.htm")
            .Replace("<%Reservation%>", transaction.Reservation)
            .Replace("<%FullName%>", transaction.FullName)
            .Replace("<%SaleDate%>", transaction.SaleDate.ToShortDateString())
            .Replace("<%Amount%>", transaction.Amount.ToString("C2"))
            .Replace("<%Invoice%>", transaction.Invoice)
            .Replace("<%Confirmation%>", transaction.Confirmation)
            .UsingClient(new SmtpClient(_mailhost));
email.Send();

 

The HTML inside TransactionTemplate.htm has the below code. This is a basic example but demonstrates the usage of the templates.

<html>
<head>
    <title></title>
</head>
<body>
    A Transaction has been made...<br />
    <table>
        <tr>
            <th>Reservation Number</th>
            <th>Customer Name</th>
            <th>Email</th>
            <th>Sale Date</th>
            <th>Amount</th>
        </tr>
        <tr>
            <td><%Reservation%></td>
            <td><%FullName%></td>
            <td><%Email%></td>
            <td><%SaleDate%></td>
            <td><%Amount%></td>
        </tr>
        <tr>
            <th>Invoice</th>
            <th>Confirmation</th>
        </tr>
        <tr>
            <td><%Invoice%></td>
            <td><%Confirmation%></td>
        </tr>
    </table>
</body>
</html>

Pretty damn easy if you ask me.

This also give me the flexibility to edit the HTML file in a proper editor instead of concatenating all those strings together. The main idea was inspired by the MailDefinition class built into .NET but I didnt like the face that: 1 it was part of the System.Web.UI.WebControls namespace when its not a Web Control and wouldnt be used as one. And 2, It attempted to use another Control as a worker for it. So I just extended MailMessage to add the functionality.

All you need to do is download FluentEmail from Github

Dropbox API and RestSharp for a C# developer

The Dropbox API has foiled my development of “Droppedboxx” for some time now. Mainly because the on site documentation is average for anyone not planning on developing for the iphone (dropbox, you used to be cool). Being a C# developer this was bad news for me but I decided to give it a go anyway and after a few weeks of failed attempts and a few emails to support about my issues I realised the API documentation on the dropbox website is slightly wrong.

So now that I have it working I thought i’d share this with my fellow .NET developers (and anyone else who will care to listen).

What you will need (What i used):

  • RestSharp (What would i do without you…)
  • Oauth Library for C# (this one seems the easiest to use with RestSharp)
  • Access to the Dropbox API and an APIKey/Secret for your app

I guess I should start with the Setup of it, I started this as a Windows Mobile application but I switched to a .NET library so I could test it easier and also use RestSharp (might open source the library if I get around to it).

 

Login, this is possibly the easiest method if you have the right URL (the API documentation give you the wrong URL). We should be using “https://api.getdropbox.com/0/token” Code you say?

Update: Got some helpful advice about using the request.Resource property with parameters instead of a string.format.

public UserLogin Login(string email, string password)
{
    var restClient = new RestClient("https://api.getdropbox.com");
    var request = new RestRequest(Method.GET);
    request.Resource = "{version}/token";
    request.AddParameter("version", _version, ParameterType.UrlSegment);

    request.AddParameter("oauth_consumer_key", _apiKey);

    request.AddParameter("email", email);
    request.AddParameter("password", password);

    var response = restClient.Execute<UserLogin>(request);

    _userLogin = response.Data;

    return response.Data;
}

Seems pretty easy right? Most of thats RestSharp (with the JSONDeserializer), I then have the UserLogin class which is returned by this function.

public class UserLogin
{
    public string Token { get; set; }
    public string Secret { get; set; }
}

And now your user is logged in, you will then want to store the Users Token and Secret (storing UserLogin object would probably be better) as its used for the rest of the requests. I’ll just show you how to get the User Info as the other requests differ from the login (that is where we’ll need the Oauth stuff).

 

Account/Info, I’ll start with the Response classes this time…

public class AccountInfo
{
    public string Country { get; set; }
    public string Display_Name { get; set; }
    public QuotaInfo Quota_Info { get; set; }
    public string Uid { get; set; }
}

public class QuotaInfo
{
    public string Shared { get; set; }
    public string Quota { get; set; }
    public string Normal { get; set; }
}

These are the classes for RestSharp to Deserialize the JSON to. Pretty simple, this is straight off the API Documentation site. Now for the actual request…

public AccountInfo Account_Info()
{
    var restClient = new RestClient("http://api.dropbox.com");
    OAuthBase oAuth = new OAuthBase();
    string nonce = oAuth.GenerateNonce();
    string timeStamp = oAuth.GenerateTimeStamp();
    string normalizedUrl;
    string normalizedRequestParameters;
    string sig = oAuth.GenerateSignature(new Uri(string.Format("{0}/{1}/account/info", restClient.BaseUrl, _version)),
        _apiKey, _appsecret,
        _userLogin.Token, _userLogin.Secret,
        "GET", timeStamp, nonce, out normalizedUrl, out normalizedRequestParameters);
    sig = HttpUtility.UrlEncode(sig);

    var request = new RestRequest(Method.GET);
    request.Resource = string.Format("{0}/account/info", _version);
    request.AddParameter("oauth_consumer_key", _apiKey);
    request.AddParameter("oauth_token", _userLogin.Token);
    request.AddParameter("oauth_nonce", nonce);
    request.AddParameter("oauth_timestamp", timeStamp);
    request.AddParameter("oauth_signature_method", "HMAC-SHA1");
    request.AddParameter("oauth_version", "1.0");
    request.AddParameter("oauth_signature", sig);

    var response = restClient.Execute<AccountInfo>(request);

    return response.Data;
}

Now, this request has a different Base Url to the Login function and this uses OAuth. So firstly we create an instance of the RestSharp client and the OAutheBase classes. The OAuthBase class then handles all of the oauth parameters that we need to make the Request (nonce, timestamp, etc.). GenerateSignature is the main 1 that rolls up the request path and the tokens/secrets into a hash for extra security. As for the dropbox part of this, we dont need any extra parameters as we are just getting the logged in users Account Info. Then its just a matter of adding these parameters to the RestRequest object, notice the oauth_token is actually the UserLogin.Token that we got from our Login method?

 

Stay tuned for more, Tales of interest!

 

Update: I have added a Part 2 to this post.

Parallel vs. Async Email performance

Lately i have been doing some work with sending emails and I have chosen to use some of .NET4.0’s Parallel processing and the FluentEmail library.

I then got thinking about the Performance differences between using Parallel against Asynchronous so I decided to run some tests. Based of the C# Micro Performance Testing Class blog post I have written a simple app for testing this.

 

Firstly I created my base class for the performance tests, this will later be inherited by by different testers.

Pretty basic, methods to start and finish that set the stopwatch and the finished bool…

public abstract class PerformanceTester
{
    public TimeSpan TotalTime { get; private set; }
    public TimeSpan AverageTime { get; private set; }
    private Stopwatch _stopwatch;
    public int Iterations { get; private set; }
    public bool Finished { get; private set; }

    public PerformanceTester()
    {
        Finished = false;
    }
    public virtual void Run(int iterations)
    {
        Iterations = iterations;
        _stopwatch = Stopwatch.StartNew();
    }
    public void Finish()
    {
        _stopwatch.Stop();
        AverageTime = new TimeSpan(_stopwatch.Elapsed.Ticks / Iterations);
        TotalTime = _stopwatch.Elapsed;
        Finished = true;
    }
}

Now that we have the stone to build off we can create the classes for the tests we went to perform, in this case 1 for Async and 1 for Parallel (below) these 2 classes then just override the Run method and call Finish when its done. However the async class differs slightly as it has to wait for the async callback.

 

public class ParallelEmails : PerformanceTester
{
    public override void Run(int iterations)
    {
        base.Run(iterations);
        //do stuff here
        Parallel.For(0, iterations, i =>
            {
                //send emails
                Email.From(Settings.FromAddress)
                    .To(Settings.ToAddress)
                    .Subject(Settings.Subject + "P" + i)
                    .Body(Settings.Body)
                    .UsingClient(new SmtpClient(Settings.HostAddress))
                    .Send();
            });
        //all done
        base.Finish();
    }
}
public class AsyncEmails : PerformanceTester
{
    private int _currentIteration;

    public override void Run(int iterations)
    {
        base.Run(iterations);
        //do stuff here
        for (int i = 0; i < iterations; i++)
        {
            Email.From(Settings.FromAddress)
                        .To(Settings.ToAddress)
                        .Subject(Settings.Subject + "A" + i)
                        .Body(Settings.Body)
                        .UsingClient(new SmtpClient(Settings.HostAddress))
                        .SendAsync(emailCallback);
        }
    }
    private void emailCallback(object sender, EventArgs e)
    {
        _currentIteration = _currentIteration + 1;
        if (_currentIteration == Iterations) Finish();
    }
}

Now for some results, now I know the performance will differ depending on PC hardware so I’m testing this on 2 different PCs the first has a Core 2 Duo E6400 @ 2.13GHz and the 2nd has a Core 2 Quad Q6600 @ 2.4GHz.

Table represents my results of the tests (in milliseconds)

Emails Sent Parallel (Dual Core) Parallel (Quad Core) Async

(Dual Core)
Async

(Quad Core)
1 6019 5716 6107 7039
8 26642 39571 34291 25638
15 59282 46897 58849 47730
20 70977 67488 62120 79164

Just a note, these tests were run from the 2 PCs on the same network with the same internet connection connecting to gmails smtp server. Of course there are other factors that would affect performance of these 2 methods but this is just to get an idea of the differences.

Read into that as much as you want but the main thing i got from it is parallel processing is better when you have more cores (which isn’t really anything new at all). Good to see the difference in results though.

 

DK out…