Referral or Affiliate Code tracking Module in ASP.NET

I recently had a need to track referral codes for ad campaigns as they came in through Google ad clicks. This same module can easily be used to handle affiliate codes as they come in. This module will be executed at each request, will check for a query variable, and store the value in a cookie. You can then retrieve the cookie where ever you need it in you app.

The HTTP Module

/// <summary>
/// Adds a cookie to the clients machine to track what ref code they came in through
/// </summary>
public class RefCodeExtraction : IHttpModule
{
    public void Dispose()
    {
    }
    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }
    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context = (sender as HttpApplication).Context;
       //replace "q" with your variable name
        if (!String.IsNullOrEmpty(context.Request.QueryString["q"]))
            SaveRefCodeToCookie(context.Request.QueryString["q"], context);
        else//ADD A DEFAULT CODE
            SaveRefCodeToCookie("ORGANIC", context);
    }
    private void SaveRefCodeToCookie(string refCode, HttpContext context)
    {
        try
        {
            context.Response.Cookies.Remove("RefCookieName");
            HttpCookie c = new HttpCookie("RefCookieName");
            c.Value = refCode.EndsWith("/") ? refCode.TrimEnd('/'):refCode;
            c.Expires = DateTime.Now.AddDays(30);
            context.Response.Cookies.Add(c);
        }
        catch {}
    }
}

Activate the Module

To activate this module, place the class in your App_Code directory and add the class name to the web.config file under the HttpModules section. For example:

        <httpModules>
            <add name="RefCodeExtraction" type="RefCodeExtraction"/>
            <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        </httpModules>

It will now be executed at the beginning of each request.

Retrieve the Ref Code from the Cookie

To retrieve the code later on, just call:

string refCode = HttpContext.Current.Request.Cookies["RefCookieName"].Value;

Conclusion

How you use the referral code or ad code is up to you. In my case, the ad campaign code is attached to a google ad using the variable "q" followed by the campaign code. This is set in the ad management system and allows for our web site to track and persist which ad codes are generating conversions. This could be adapted to pay out affiliates for referrals or whatever else you might need.

The code is below.

RefCodeExtraction.cs (1.24 kb)

kick it on DotNetKicks.com


Building a Utility Library Without Going Overboard

I realized a while ago that I was breaking the "DRY" (Don't repeat yourself) rule pretty severely when looking at my various projects as a whole. I generally focus on not repeating code within an application but I have not been careful enough with this rule when starting new projects. Many times when I need a particular function that I have written before I will just go hunt it down and copy the code into my new project. It has recently become a problem because I now have the same function littered throughout my different programs with various modifications made to each!

It's time for me to build a utility library that will become the main source for commonly used functions in my applications. The main need that I saw for this library was the simple but tedious task of creating an email message and sending it along via a web contact form. Just about every .NET web site I've ever built has needed this functionality and each time I created a new MailMessage and Email class to handle the job. This was the first thing I added to my new Utility Library.

The purpose of this post is to try and get some feedback on which functions you would include in such a library and which you would leave out. The last thing I want to do is create some bloated set of code filled with methods that are rarely used. If anyone is interested in seeing how I am going about creating this library (or would like the actual library) just let me know in the comments and I'll do my best to help out.

Here is the initial structure and the classes I'm starting with:

Utility Library Structure

Each class contains a few functions that I find myself using frequently. Here is the class diagram for the few classes I have so far:

Utility Class Diagram

Again, if anyone wants the actual code or libraries let me know.

Help!

If you were building a utility library, what would you include or exclude? Anyone? Bueller...?

EDIT

I've added the source code for the project to the post for anyone interested. Since the post I've added a couple more functions to the library.

SevenLabs.Utilities.zip (35.55 kb)

kick it on DotNetKicks.com


Creating great thumbnails in ASP.NET

The built in function for creating thumbnails in ASP.NET is extremely convenient and very simple to implement.

int width = 190;
int height = 190;
Bitmap source = new Bitmap("c:\someimage.gif");
System.Drawing.Image thumb = source.GetThumbnailImage(width,height,null,IntPtr.Zero);

 original (31.7k)

The trouble is that it produces relatively poor quality results and excessively large file sizes. The thumbnails tend to look very muddy when using this route, but many times it's good enough for whatever your needs may be.

An Alternative

The alternative that I use regularly involves redrawing the image using the System.Drawing.Graphics library. It is very simple to implement but produces superior results if for no other reason than its file size. The following is the standard function I use for creating thumbnails.

public static Bitmap CreateThumbnail(Bitmap source, int thumbWi, int thumbHi, bool maintainAspect)
        {
            // return the source image if it's smaller than the designated thumbnail
            if (source.Width < thumbWi && source.Height < thumbHi) return source;

            System.Drawing.Bitmap ret = null;
            try
            {
                int wi, hi;

                wi = thumbWi;
                hi = thumbHi;

                if (maintainAspect)
                {
                    // maintain the aspect ratio despite the thumbnail size parameters
                    if (source.Width > source.Height)
                    {
                        wi = thumbWi;
                        hi = (int)(source.Height * ((decimal)thumbWi / source.Width));
                    }
                    else
                    {
                        hi = thumbHi;
                        wi = (int)(source.Width * ((decimal)thumbHi / source.Height));
                    }
                }

                // original code that creates lousy thumbnails
                // System.Drawing.Image ret = source.GetThumbnailImage(wi,hi,null,IntPtr.Zero);
                ret = new Bitmap(wi, hi);
                using (Graphics g = Graphics.FromImage(ret))
                {
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.FillRectangle(Brushes.White, 0, 0, wi, hi);
                    g.DrawImage(source, 0, 0, wi, hi);
                }
            }
            catch
            {
                ret = null;
            }

            return ret;
        }

aG8klKbxhE-h43x2_tLGsw (10.5k)

This function is handy because it's parameters include a flag for maintaining the aspect ratio of the image along with the thumbnail size you would like. The thumbnail magic happens in this portion of the code:

                using (Graphics g = Graphics.FromImage(ret))
                {
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.FillRectangle(Brushes.White, 0, 0, wi, hi);
                    g.DrawImage(source, 0, 0, wi, hi);
                }

This method is slightly slower but the results are hard to ignore as illustrated by the comparison below:

(31.7k)original aG8klKbxhE-h43x2_tLGsw (10.5k)

Pretty nice improvement in both file size and quality I would say, but....

We can do even better

Now we can add into the mix some JPEG compression and really optimize the results. I won't pretend to fully understand how the JPEG compression code below works, but it sure does the trick.

                //Configure JPEG Compression Engine
                System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();
                long[] quality = new long[1];
                quality[0] = 75;
                System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
                encoderParams.Param[0] = encoderParam;

                System.Drawing.Imaging.ImageCodecInfo[] arrayICI = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
                System.Drawing.Imaging.ImageCodecInfo jpegICI = null;
                for (int x = 0; x < arrayICI.Length; x++)
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICI = arrayICI[x];
                        break;
                    }
                }

This code will set up the encoderParameters needed for saving the new compressed thumbnail. The quality[0] value is where you set the compression level. I've had success going as low as a value of 40 for some applications, but when quality is a major requirement I find 75 to do very well. To use this engine you would execute the JPEG Compression code before you save your thumbnail, then use its encoderParamaters as a parameter when saving. For example:

	System.Drawing.Image myThumbnail = CreateThumbnail(myBitmap,Width,Height,false);                

	//Configure JPEG Compression Engine
                System.Drawing.Imaging.EncoderParameters encoderParams = new System.Drawing.Imaging.EncoderParameters();
                long[] quality = new long[1];
                quality[0] = 75;
                System.Drawing.Imaging.EncoderParameter encoderParam = new System.Drawing.Imaging.EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
                encoderParams.Param[0] = encoderParam;

                System.Drawing.Imaging.ImageCodecInfo[] arrayICI = System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders();
                System.Drawing.Imaging.ImageCodecInfo jpegICI = null;
                for (int x = 0; x < arrayICI.Length; x++)
                {
                    if (arrayICI[x].FormatDescription.Equals("JPEG"))
                    {
                        jpegICI = arrayICI[x];
                        break;
                    }
                }
	
	myThumbnail.Save(Path.Combine(SavePathThumb, fileName), jpegICI, encoderParams);
                myThumbnail.Dispose();

compressed (2.39k)

Which still looks pretty darn good for 2.39k.

Conclusion and final comparison

Here is the final comparison between the 3 thumbnails going from largest file size to smallest:

original aG8klKbxhE-h43x2_tLGsw compressed

Largest = 31.7k

Uncompressed redraw = 10.5k (67% smaller)

Compressed redraw = 2.39k (92% smaller)

It's hard to ignore those results. The source code for the thumbnail function and the JPEG compression engine are below.

ThumbnailGenerator.cs (1.97 kb)

JPEGCompressionConfig.cs (969.00 bytes)

kick it on DotNetKicks.com


How to quickly configure ASP.NET XML based authentication

The .NET framework (starting with v2.0) has some great built in role and member based authentication that is easy to tap with a little setup. Recently I was setting up some XML based user authentication for members only (no roles) and was having trouble finding a relevant guide as to how you would go about setting this up. I decided to turn to the BlogEngine.Net source for some guidance and was able to get a complete solution online without too many hang ups.

What is the goal?

The goal here is to set up basic user authentication using an XML file for user data storage and the built in .NET Login controls. The solution should tie in with the .NET MembershipProvider to take advantage of all the work that has already been done in the framework.

Steps to complete

  1. Create an XMLMembershipProvider (or download this one)
  2. Create a Users.xml file in the App_Data folder
  3. Configure the web.config to use the provider
  4. Build the Login page

Step 1. Create an XMLMembershipProvider

The first thing you need is a class that inherits from the .NET MembershipProvider which will let the program know how to interface with your data. This is probably the most complicated part, but fortunately the BlogEngine.NET code saves us a lot of time since the provider has already been written for that system. It does not make use of all of the available MembershipProvider features but is sufficient for basic authentication. You can write your own solution if you prefer but this code will help get you going, just add it to your App_Code folder.

The source code can be downloaded here: XmlMembershipProvider.cs

Step 2. Create a Users.xml file

The Users.xml file will store all of the account data for the users. Create a file under the App_Data folder and name it Users.xml.

The file should be in the following format:

<?xml version="1.0" encoding="utf-8" ?>
<Users>
    <User>
        <UserName>user1</UserName>
        <Password>password</Password>
        <Email>user1@demo.com</Email>
        <LastLoginTime>2007-12-05 20:46:40</LastLoginTime>
    </User>
    <User>
        <UserName>user2</UserName>
        <Password>password</Password>
        <Email>user2@demo.com</Email>
        <LastLoginTime>2007-12-05 20:46:40</LastLoginTime>
    </User>
</Users>

Step 3. Configure the web.config file to use the provider

The web.config file needs to be set up to know how to access your custom membership provider from step 1.

First set the authentication mode to "Forms" and configure the form values. You should set the name= attribute to the cookie name you want to use for the authentication. You should set the loginURL= attribute to the virtual path to your login page.

Next add the membership section and set the defaultProvider= attribute to the name of your provider class. Add a provider to the section and set the type= attribute to the namespace or class name of your xml provider. Finally, specify the xmlFileName= attribute by setting it to the virtual path of the Users.xml file

<system.web>
        <!-- Authentication -->
        <authentication mode="Forms">
            <forms timeout="129600" name=".XMLAUTH" protection="All" slidingExpiration="true" loginUrl="~/Login/Default.aspx" cookieless="UseCookies"/>
        </authentication>
        <membership defaultProvider="XmlMembershipProvider">
            <providers>
                <clear/>
                <add name="XmlMembershipProvider" type="XmlMembershipProvider" description="XML membership provider" xmlFileName="~/App_Data/Users.xml"/>
            </providers>
        </membership>
</system.web>

Step 4. Build the Login Page

Now that your application is wired up to use your new provider and xml data file, you need to set up a page for the user to authenticate from. This is normally done at ~/Login.aspx or at ~/Login/Default.aspx. I prefer the latter as it's easier for people to remember a path without an extension.

Create your page and drag an <asp:Login /> control from your toolbar onto the page. At minimum you need to specify a DestinationPageUrl= for your user to be redirected to upon successful sign in, and an OnAuthenticate method. To assign the OnAuthenticate method, double click on the control in the design view to be taken to the method in the code view.

On the page, include the System.Web.Security library to easily access the Membership class.  You can now use the following simple method to authenticate a user: (this is in the code behind of the login page)

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

public partial class Login_Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
    {
        if (Membership.ValidateUser(Login1.UserName, Login1.Password))
            FormsAuthentication.RedirectFromLoginPage(Login1.UserName, true);
    }
}

The Membership.ValidateUser() function will access your XmlMembershipProvider which will check the supplied credentials against the xml file. If the username and password matches it will return true. If they provided the proper credentials, you can use the FormsAuthentication.RedirectFromLoginPage() function to add a cookie to the browser indicating the person has been authenticated. The second parameter (true) indicates that you do want the cookie to be placed on the browser so that the authentication carries throughout the site.

How to determine authentication on the site

Now that you have the user authentication taken care of, it's a simple task to determine if a user is logged in on the rest of your sites pages. To find out, you can simply call the following function:

 if (Page.User.Identity.IsAuthenticated)
 {  
	//User is logged in
 }
 else
 {  
	//User is not logged in 
 }

You can also make use of the other built in .NET login controls like <asp:LoginStatus />, <asp:CreateUserWizard />, <asp:PasswordRecovery /> etc.

kick it on DotNetKicks.com


Another .NET Google Charts API Wrapper

I know there are other C# Google Chart solutions out there but none of them seemed to accomplish what I needed for my particular situation. They all seem to be geared towards generating simple graphs from a small static data set.

I wanted something that would make it easy to generate graphs from large datasets in a variety of ways while keeping things clean when the data became questionable. My focus was aimed at financial analysis over a specified period of time, generally dealing with revenue numbers. For this reason, my example will demonstrate how to use the API to create a graph showing spend vs. revenue over time.

I was able to create a GoogleChart class that basically aids in generating the Google URL syntax and combine that with a GraphHelper class that scrubs up the data for use with the charts. The GoogleChart class will simply return a string with the url to the google chart you want generated.

The source code for the API and Helper class is provided below, and I will simply show how to code against them.

   1:  GoogleChart chartRevenue = new GoogleChart();
   2:  chartRevenue.GraphWidth = width;
   3:  chartRevenue.GraphHeight = height;
   4:  chartRevenue.ChartTitle = "Revenue vs Spend";
   5:  chartRevenue.ChartType = ChartTypes.LineChart;
   6:  //The Legend is stored in a List and expects a set of data points per legend.
   7:  chartRevenue.ChartLegend.Add("Revenue Dollars");
   8:  chartRevenue.ChartLegend.Add("Spend Dollars");
   9:  //The Colors are stored in a List and expect a set of data points per color
  10:  chartRevenue.DataSetColors.Add("#00ff00");
  11:  chartRevenue.DataSetColors.Add("#ff6614");
  12:  chartRevenue.ShowGridLines = true;
  13:   
  14:  //fake query
  15:  string query1 = "select revenue from data where date between now and later";
  16:  string query2 = "select spend from data where date between now and later";
  17:   
  18:  DataSet dsRev = DataAccess.ExecuteQueryGetDataSet(query1);
  19:  DataSet dsSpend = DataAccess.ExecuteQueryGetDataSet(query2);
  20:   
  21:  int MaxRev = 0;
  22:  int MaxSpend = 0;
  23:   
  24:  //The MaxValue is needed to scale the results to the graph. 
  25:  //The second parameter is the column to use for the dollar value. 
  26:  try { MaxRev = GraphHelper.GetMaxValueFromDataSet(dsRev, 2); }
  27:  catch { MaxRev = 0; }
  28:   
  29:  try { MaxSpend = GraphHelper.GetMaxValueFromDataSet(dsSpend, 0); }
  30:  catch { MaxSpend = 0; }
  31:   
  32:  if (dsRev != null && dsRev.Tables[0].Rows.Count > 0)
  33:  {
  34:  //Set the MaxValue for the chart to get the proper scale.
  35:  chartRevenue.MaxValue = MaxRev > MaxSpend ? MaxRev : MaxSpend;
  36:  //The GraphHelper will grab out a range of X Axis Labels 
  37:  //using your max value as the top most value.
  38:  //The additional x axis values are generated by dividing out the
  39:  //max value to get intermediate values.
  40:  foreach (string s in GraphHelper.GetAxisLabelsFromDataSet(dsRev, 1))
  41:  {
  42:  chartRevenue.XAxisLabels.Add(s);//Add the labels to the chart
  43:  }
  44:  //Add the revenue data points to the collection using the
  45:  //Graph Helper, the second parameter is the column holding the data values.
  46:  chartRevenue.DataPoints.Add(GraphHelper.GetDataValuesFromDataSet(dsRev, 2));
  47:  }
  48:  else
  49:  {
  50:  //No Data, return no data image
  51:  return "~/Images/Layout/Tracker_LCD_No_Data.jpg";
  52:  }
  53:   
  54:  //Add the Spend data points to the chart
  55:  if (dsSpend != null && dsSpend.Tables[0].Rows.Count > 0)
  56:  chartRevenue.DataPoints.Add(GraphHelper.GetDataValuesFromDataSet(dsSpend, 0));
  57:   
  58:  //Tell the graph to build with the supplied data and
  59:  //return a URL to the resulting chart image.
  60:  return chartRevenue.GetGraphImage();

The resulting graph looks like the following:

 The Source code is below. I'm sure this solution is not for everyone, but people with large data sets might find it useful.

Source: GoogleChart.zip (3.70 kb)

kick it on DotNetKicks.com


Search

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008