Author Archive

WCF REST Starter Kit to Constant Contact Sample Application

I’ve had a few requests to get more details on how I used the WCF REST Starter Kit to integrate with the Constant Contact AtomPub API (posted here). It’s shamefully simple but here it is. If you are not satisfied I will give you your money back.

Requires:

Download Sample App

Notes:

  • Update the app.config with your account settings from Constant Contact.
  • Sample is using Http Digest for authentication. If there is interest I can update for OAuth.
using System;
using System.Configuration;
using System.Collections.Generic;
using Freemind.Domain.Email;

namespace Freemind.ConstantContactSample
{
   class Program
   {
      static void Main(string[] args)
      {
         try
         {
            Console.WriteLine("Begin loading Contact Lists");

            // Connect and list Contact Lists
            ContactListProvider provider = new ContactListProvider(
               ConfigurationManager.AppSettings["ConstantContactAccountName"],
               ConfigurationManager.AppSettings["ConstantContactPassword"]);

            List lists = provider.GetContactLists();

            if (lists != null)
            {
               foreach (ContactList list in lists)
               {
                  Console.WriteLine("  list:{0} id:{1}", list.Name, list.id);
               }
            }

            Console.WriteLine("Completed loading Contact Lists");
            Console.WriteLine("Press any key to quit");
            Console.ReadKey();
         }
         catch (Exception ex)
         {
            Console.WriteLine("Error: {0}",ex.Message);
         }
      }
   }
}

Using WCF REST Starter Kit to Access Constant Contact API

Problem: Allow customers to sign up for email newsletters and synchronize those accounts with Constant Contact.

Constant Contact is a company that provides Email Marketing services. You can use their editor to create nice looking emails and then configure a list of contacts. Constant Contact handles all the email sending, bounces, and unsubscribe activities. They also provide excellent reporting features to track how many people actually view your emails or click on the contained links.

The nice folks at Constant Contact provide a set of web services for accessing account data. These interfaces are implemented as REST services utilizing the Atom Publication Protocol (AtomPub) and Atom Syndication Format. At first it might seem odd to be accessing data objects via a blogging protocol but it actually works quite naturally. So well that Google’s GData and Microsoft’s ADO.NET Data Services also support this technique.

Writing all the HTTP, Auth, and XML parsing code can be a real drag. Luckily Microsoft is releasing a WCF REST Starter Kit. From the CodePlex summary:

The WCF REST Starter Kit is a set of .Net Framework classes and Visual Studio features and templates that enable users to create and access REST-style Windows Communication Foundation (WCF) services. These services are based on the WCF web programming model available in .Net 3.5 SP1. The starter kit also contains the full source code for all features, detailed code samples, and unit tests.

This starter kit makes accessing the Constant Contact API quite simple. Call GetEntry or GetFeed with a URI and get back a SyndicationItem or SyndicationFeed (collection of items). Similar methods exist for adding and updating data (AddEntry and UpdateEntry).

The following is one method from my ContactListProvider class:

public ContactList GetContactList(string id)
{
   AtomPubClient client = new AtomPubClient();
   client.TransportSettings.Credentials = GetLoginCredentials();

   // Place in a try block to ensure that any errors are caught
   try
   {
      SyndicationItem item = client.GetEntry(new Uri(id));

      ContactList list = new ContactList(item.Content as XmlSyndicationContent);

      return list;
   }
   catch (WebException ex)
   {
      _log.Error("WebException: " + ex.Status + " " + ex.Message, ex);
      return null;
   }
   catch (Exception ex)
   {
      // Get the exception type
      _log.Error("Exception: " + ex.Message, ex);
      return null;
   }
}

Contact me via comments if you want a copy of all the providers.

How to convert UPC-A to zero-compressed UPC-E

Universal Product Codes (UPCs) should be a very straight-forward topic. You give a product a number and that is the end of the story. Unfortunately it turns out to be a bit more complicated than that.

There is a great (well I guess that’s subjective) page on Wikipedia that does an excellent job explaining all the variations: Universal Product Codes. There are 14, 13, 12, 11, 10, 8, and 6 digit types of UPCs. There is also an interesting algorithm to convert from a fairly standard 12-digit UPC-A code into a zero-compressed UPC-E. This is a most uninteresting fact. Until the day comes when you actually have to convert some of these codes.

Here is some C# code to:

  • Convert UPC-A codes to UPC-E
  • Convert UPC-E codes to UPC-A
  • Calculate check digits

If google brought you here because you need these conversions then bask is it’s yucky goodness! If not, then just move right along (basically it’s a cup with dirt in it).

public string ConvertUPCAToUPCE(string UPCa)
{
   string UPCe = "";

   //Must be 12 digits
   if (UPCa.Length != 12)
   {
      return "";
   }

   string mfg = UPCa.Substring( 1, 5);
   string prod = UPCa.Substring(6, 5);

   if ((mfg.Substring(2) == "000") || (mfg.Substring(2) == "100") ||(mfg.Substring(2) == "200") )
   {
   //  0            XXNNN0  0XX000-00NNN + check
   //  1            XXNNN1  0XX100-00NNN + check
   //  2            XXNNN2  0XX200-00NNN + check
      UPCe = mfg.Substring(0, 2) + prod.Substring(2, 3) + mfg.Substring(2, 1);
   }
   else if (mfg.Substring(3,2) == "00")  
   {
   //  3            XXXNN3  0XXX00-000NN + check
      UPCe = mfg.Substring(0,3) + prod.Substring(3,2) + "3";
   }
   else if (mfg.Substring(4, 1) == "0")
   {
   //  4            XXXXN4  0XXXX0-0000N + check
      UPCe = mfg.Substring(0, 4) + prod.Substring(4,1) + "4";
   }
   else
   {
   //  5            XXXXX5  0XXXXX-00005 + check
   //  6            XXXXX6  0XXXXX-00006 + check
   //  7            XXXXX7  0XXXXX-00007 + check
   //  8            XXXXX8  0XXXXX-00008 + check
   //  9            XXXXX9  0XXXXX-00009 + check
      UPCe = mfg + prod.Substring(4);
   }

   return UPCe;
}

public string ConvertUPCEToUPCA(string UPCe)
{
   //Must be 12 digits
   if (UPCe.Length != 6)
   {
      return "";
   }

   string mfg = "";
   string prod = "";

   switch (UPCe.Substring(5, 1))
   {
      case "0":
         mfg = "0" + UPCe.Substring(0, 2) + "000";
         prod = "00" + UPCe.Substring(2, 3);
         break;

      case "1":
         mfg = "0" + UPCe.Substring(0, 2) + "100";
         prod = "00" + UPCe.Substring(2, 3);
         break;

      case "2":
         mfg = "0" + UPCe.Substring(0, 2) + "200";
         prod = "00" + UPCe.Substring(2, 3);
         break;

      case "3":
         mfg = "0" + UPCe.Substring(0, 3) + "00";
         prod = "000" + UPCe.Substring(3, 2);
         break;

      case "4":
         mfg = "0" + UPCe.Substring(0, 4) + "0";
         prod = "0000" + UPCe.Substring(4, 1);
         break;

      default:
         mfg = "0" + UPCe.Substring(0, 5);
         prod = "0000" + UPCe.Substring(5, 1);
         break;
      }

   return mfg + prod + CalculateCheckDigit(mfg + prod);
}

//In the UPC-A system, the check digit is calculated as follows:
//
//  1. Add the digits in the odd-numbered positions (first, third, fifth, etc.) together and multiply by three.
//  2. Add the digits in the even-numbered positions (second, fourth, sixth, etc.) to the result.
//  3. Find the result modulo 10 (i.e. the remainder when the result is divided by 10).
//  4. If the result is not zero, subtract the result from ten.
//
private string CalculateCheckDigit(string upc)
{
   int check = 0;
   char[] chars = upc.ToCharArray();

   // process string from right to left
   Array.Reverse(chars);

   for (int i =0;i<chars.Length;i++)
   {
      if ((i % 2)!= 0)
      {
         // even
         check += int.Parse(chars[i].ToString());
      }
      else
      {
         // odd
         check += (3 * int.Parse(chars[i].ToString()));
      }
   }

   check = check % 10;

   if (check != 0)
   {
      check = 10 - check;
   }

   return check.ToString();
}

Update: CheckDigit right to left processing.

Beware of Windows Server 2008 Network Optimization

Problem:

Recently I was migrating some Windows Server 2003 applications to new hardware and Windows Server 2008. The applications were mostly simple ASP.NET applications using either NHibernate or ADO.NET to communicate to a Windows Server 2003 x64 box running Sql Server 2005sp3.

Each of the new Win2k8 machines were having performance issue retrieving data from the database. After firing up Sql Profiler I was able to see that executing a simple statement like “SELECT * FROM Store” was taking over 5sec to return 40 rows. The slowness was the same in both ADO.NET and NHibernate apps. More research from that the WinForms apps running as admin were having the same issues. And executing the queries in SSMS showed same behavior.

So Win2k3 servers talking to the same database are running fast. Blowing my mind I found that using the Win2k8 servers to connect to a different Sql Server 2005sp2 on 32bit runs just fine (no delays). That’s right I can load the data onto a different server and see no delays!

If you are reading this you might be starting to form some theories. Both the Win2k3 and Win2k8 servers are on the same subnet.

At this point I requested help from the smart support guys from Microsoft. We verified that the Sql execution plans were not changing. We also verified the network settings on the database, Win2k3, and Win2k8 servers.

Next step netmon. After capturing and reviewing the netnom logs we were able to see that there were packets getting fragmented and there were long delays in sending the secondary packets. There are many newer features in Win2k8 to optimize the network performance (receive side scaling, tcp chimney offload, network direct memory access, and autotuning). Learn more here: http://support.microsoft.com/kb/951037

Root Cause:

In my situation the autotuning feature was trying to adjust the tcp receive window (RWIN) and was timing out waiting for an acknowledgment from the database server. It is possible that the router was holding onto the response packet, I wasn’t able to completely correlate all the send/receives.

Learn more about RWIN behavior here: http://www.nerdgrind.com/speed-up-windows-vista-network-performance-with-tcp-windows-tuning/2/

Resolution:

It is possible that we could have reconfigured the router to increase the frame sizes to prevent the packet fragmentation. It is also possible that change could have hidden the delays

The way I choose to resolve the issue was to turn off the autotuning with the following statement:

netsh interface tcp set global autotuning=disabled

Some network card drivers have the ability to control this setting via the advanced properties.

Using Email Templates With ASP.NET

Somedays working with ASP.NET can be a treat. Especially on those days when you stumble upon some functionality you had no idea existed.

It seems like every website has some need to generate emails for things like order confirmation, password resets, or news updates. System.Net.Mail provides excellent functionality for building email messages and communicating with SMTP servers. But it is up to the developer to build the actual content of the message programmatically, usually by using a StringBuilder to concatenate a bunch of user-specific and common strings.

Turns out there is class and technique that can simplify that code quite a bit. MailDefinition is a class that allows you to build email templates and easily update the dynamic parts.

Check out the following code:

public void GenerateOrderEmailCustomer(Order order)
{
   try
   {
      MailDefinition template = new MailDefinition();
      template.BodyFileName = "~/templates/CustomerOrderConfirmation.html";
      template.From = order.Store.Retailer.ApplicationConfig.EmailOrderFromAddress;

      Dictionary<string, string> data = new Dictionary<string, string>();
      data.Add("<<order.OrderNumber>>", order.OrderNumber.ToString());
      data.Add("<<order.Store.Name>>", order.Store.Name);
      data.Add("<<order.Status>>", order.Status.ToString());
      data.Add("<<order.FirstName>>", order.FirstName);
      data.Add("<<order.LastName>>", order.LastName);
      data.Add("<<order.Address1>>", order.Address1);
      data.Add("<<order.Address2>>", order.Address2);
      data.Add("<<order.City>>", order.City);
      data.Add("<<order.State>>", order.State);
      data.Add("<<order.Zip>>", order.Zip);
      data.Add("<<order.PaymentMethod>>", order.PaymentMethod.ToString());
      data.Add("<<order.Payment.CardHoldersName>>", (order.Payment != null) ? order.Payment.CardHoldersName:"n/a");
      data.Add("<<order.Payment.CardType>>", (order.Payment != null)? order.Payment.CardType:"n/a");
      data.Add("<<order.Payment.CreditCardNumber>>", (order.Payment != null) ? order.Payment.CreditCardNumber.Substring(0, 4) + " **** **** ****" : "n/a");

      string detailTable = "";
      foreach (OrderDetail detail in order.Details)
      {
         detailTable += string.Format("<tr><td>{0}</td><td>{1}</td></tr>", detail.Quantity, detail.Name);
      }

      data.Add("<<order.Details>>", detailTable);
      data.Add("<<url>>", "http://www.wineconnect.com/");

      MailMessage message = template.CreateMailMessage(order.Email, data, new LiteralControl());
      message.IsBodyHtml = true;
      message.Subject = string.Format("Order Confirmation: {0} ({1})", order.Store.Name, order.OrderNumber.ToString());

      SmtpClient client = new SmtpClient(order.Store.Retailer.ApplicationConfig.EmailServer, order.Store.Retailer.ApplicationConfig.EmailPort);

      //Enable SSL
      client.EnableSsl = order.Store.Retailer.ApplicationConfig.EmailEnableSsl;

      client.UseDefaultCredentials = false;
      client.Credentials = new NetworkCredential(order.Store.Retailer.ApplicationConfig.EmailUsername, order.Store.Retailer.ApplicationConfig.EmailPassword);

      client.SendCompleted += new SendCompletedEventHandler(MailDeliveryComplete);
      client.SendAsync(message, message);
   }
   catch (Exception ex)
   {
      _log.Error("Error caught while sending customer order confirmation", ex);
   }
}

Combined with this template:

<html>
<head>
<title>Order Confirmation</title>
</head>
<body>
<table width="400" border="1" cellpadding="4" cellspacing="0" bordercolor="EDF5E3">
<tr bgcolor="#999999" >
<td align="center" style="height: 16px"><span color="#FFFFFF">Order Confirmation (<<order.OrderNumber>>)</span></td>
</tr>
<tr><td>
<<order.FirstName>>,<br><br>
This email confirms your order: <br>
Store: <<order.Store.Name>><br><br>
Order Status: <<order.Status>><br>
<br>Billing Information: <br>
Customer: <<order.FirstName>> <<order.LastName>><br>
Address1: <<order.Address1>><br>
Address2: <<order.Address2>><br>
City: <<order.City>><br>
State: <<order.State>><br>
Zip: <<order.Zip>><br>
<br>Payment Method: <<order.PaymentMethod>><br>
Card Holder: <<order.Payment.CardHoldersName>><br>
Card Type: <<order.Payment.CardType>><br>
Card Number: <<order.Payment.CreditCardNumber>><br>
Order Information:
<table class="messageTxt"><tr><td>Qty.</td><td>Item</td></tr>
<<order.Details>>
</table><br/>
Your order number is <strong><a href='<<url>>/OrderSummary.aspx?OrderNumber=<<order.OrderNumber>>'><<order.OrderNumber>></a></strong>.
Please feel free to contact us with any questions or visit our website if you need directions to our store.<br><br>
<a href="<<url>>"><strong><<url>></strong></a><br><br>
Thank you,<br><br>Wine Store Dudes<br></td></tr>
</table>
</body>
</html>

It generates this email:

CropperCapture[43]

Good Stuff:

Using this technique I was able to get rid of a bunch of string concatenation and string.Format code. The template should be much easier to maintain going forward.

Lame:

The way MailDefinition replaces the dynamic tokens is nice but a bit limiting. It might be fun to try using a Server.Execute method to pick up other ASP.NET goodness like data binding and advanced logic.

The CreateMailMessage function also requires a OwnerControl to be passed in. They could have given us an overloaded version that didn’t require this because I doubt anyone ever uses it.