Creating A Reusable AdRotator ASP.NET User Control Using LINQ
I recently had the need for an AdRotator on one of my websites. ASP.NET already comes with an AdRotator server control but since I wanted to make it even easier to reuse, I decided to create an ASP.NET user control that leverages the ASP.NET AdRotator server control, but handles all the lower-level details.
Requirements
One of my requirements is that I wanted to be able to have multiple instances of this user control, perhaps even on the same page. I also wanted to be able to either have an instance have its own pool of ad content, or share the ad content with another instance. Since the built-in AdRotator control has a Keyword feature, that would be how I would control which pool of ad content an instance of the user control displayed content from.
Additionally, I wanted the ad content coming from the database, as opposed to a configuration (XML) file, which the built-in AdRotator server control allows as of ASP.NET 2.0. This way I could add new content right into the database.
Another requirement was that I didn't want the AdRotator hitting the database every time the page was refreshed. For this, I planned to use the Cache object that exists in ASP.NET.
Another requirement was that I wanted the ability to be able to temporarily disable an ad without having to remove it from the database, and I wanted to be able to provide effective and expiration dates for an ad so that they could come online and go offline automatically as scheduled.
My last requirement was that I wanted to use LINQ to SQL for the database access.
Creating an AdRotator user control using the built-in AdRotator server control and LINQ made this all fairly trivial to accomplish. Here is how I did it.
Creating the LINQ to SQL DBML File
First, lets start with my data model. Here is what my SQL Server database table looked like:

As you can see, I have all the fields needed for an ad including an image URL, navigation URL, keyword, enabled, effective date, and expiration date.
Next, in Visual Studio 2008, I created a LINQ to SQL DBML file for my AdRotatorContent table. To do this, right-click on your Project in the Solution Explorer window and select the Add | New Item menu. Select the LINQ to SQL Classes template and give the new item the name of your database. In this example, I will use Storefront for the name of my new item. This will create a file named Storefront.dbml in my project. This also means that I will have a derived DataContext class named StorefrontDataContext that I will use to access the database with LINQ to SQL.

Once the desginer canvas window is open, find your database in the tree of Data Connections in the Server Explorer window. You may need to add a connection to your database by right-clicking on the Data Connections node and selecting the Add Connection menu item. Once you have located (or created) your database in the Data Connections node, expand the Tables node. Drag the AdRotatorContent table node into the designer canvas as seen in the image above. Save your DBML file. This will allow you to access the AdRotatorContent table with your derived DataContext class, StorefrontDataContext in my case, using LINQ to SQL.
Creating the AdRotatorControl User Control
Next, I need to create the user control. I will name it AdRotatorControl. Here is the markup in the AdRotatorControl.ascx file:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AdRotatorControl.ascx.cs" Inherits="GPCStoreFront.UserControls.AdRotatorControl" %>
That's all there is to the markup. This is really trivial so far. I simply set the NavigateUrlField, AlternateTextField, and ImageUrlField properties to the names of the columns in the database where those values will come from.
Now let's take a look at the code-behind in the AdRotatorControl.ascx.cs file:
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace [YOUR NAMESPACE GOES HERE]
{
public partial class AdRotatorControl : System.Web.UI.UserControl
{
public string Keyword { get; set; }
public string Target { get; set; }
public string CacheName { get { return String.Format("AdContent-{0}", Keyword); } }
Please notice that you will need to update the namespace with your namespace. Notice that I have added a using directive for the System.Data.SqlClient namespace since I will be using LINQ to SQL.
The only other significance of the code so far is that I have added three public properties: Keyword, Target, and CacheName.
I will use the Keyword property as a filter on the records from the AdRotatorContent table Keyword column. This is what will allow me to have multiple instances of the AdRotatorControl user control that either share the ad content or have independent content. I have made this a public property so that I can set the property in the markup for the AdRotatorControl user control. This will allow me to add the user control to a page without adding any code to the page to configure the user control.
I will use the Target property merely as a passthrough to set the AdRotator server control's Target property. This will dictate where the navigation URL will be loaded when the ad image is clicked. Use _blank to cause the URL to be opened in a new browser window. Use _self to open the URL in the current browser window. Of course you can use any valid target, but those are the standard ones that make the most sense. In my case, I will always use _blank because it is one of my self imposed web developement rules that no link should ever navigate away from your own site. This is also a public property so that it can be set in the markup thereby preventing the requirement of adding code.
I will use the CacheName property to retrieve the key name I will use to store and retrieve the ad content data from the Cache. Please notice that this property is a read-only property as there is no set statement. I wanted a property for the key name to make sure it was used consistently through the code. Also please notice that the key name is dependent on the Keyword property thereby establishing it's uniqueness in the Cache. You may want to make this property private, but I made it public just in case I want to access it from outside the class for debugging purposes.
Next, let's take a look at the Page_Load method:
protected void Page_Load(object sender, EventArgs e)
{
this.AdRotator1.Target = Target;
AdRotatorContent[] data = GetAdData();
this.AdRotator1.DataSource = data;
this.AdRotator1.DataBind();
this.Visible = data.Length > 0;
}
First, please notice that I am setting the AdRotator server control's Target property based on the user control's Target property. This is what I meant earlier when I said I was using it as a passthrough. Its sole purpose is to allow the AdRotator server control's Target property to be set from the user control's markup preventing the need to actually write any code to add an instance of the user control to a page.
Next I call the GetAdData mefhod. This is where all the real work will be done as well as caching. For now, we'll just assume it returns an array of ad content matching the Keyword specified in the user control's markup regardless of whether that data comes from the database or cache.
Then I simply bind the AdRotator server control to the array of ad content data and set the visibility of the user control depending on whether there is any ad content. This allows the user control to simply disappear if no ads exist for the specified Keyword that are enabled and valid for the current date and time.
Now let's take a look at the GetAdData method:
private AdRotatorContent[] GetAdData()
{
// Attempt to retrieve the ad content data from the cache.
AdRotatorContent[] data = Cache[CacheName] as AdRotatorContent[];
if (data == null)
{
using (StorefrontDataContext dc = new StorefrontDataContext())
{
DateTime now = DateTime.Now;
data = dc.AdRotatorContents
.Where(arc => arc.Keyword.Equals(Keyword) && arc.Enabled == true &&
arc.EffectiveDate <= now && arc.ExpirationDate >= now)
.ToArray();
Cache.Insert(CacheName, data, null, DateTime.Now.AddMinutes(5), TimeSpan.Zero,
System.Web.Caching.CacheItemPriority.NotRemovable, null);
}
}
return data;
}
}
}
I am hoping you are blown away by how trival this method turned out to be. First, I attempt to retrieve the ad content array from the Next, I simply store the current time in a variable since I will reference it in more than one spot, just to ensure consistency of it.
All that's left is to add the user control to a page. Here is the markup to do that:
While it was one of my requirements to use LINQ to SQL, nothing says that you must. Should you prefer to use ADO.NET rather than LINQ to SQL, simply replace the LINQ to SQL code with ADO.NET code retrieving a DataSet from the database and store that DataSet in the Cache object instead of the array. This is a very trivial change to make.
All in all, I am very pleased with the way this AdRotatorControl turned out. I think you will find it fairly useful and simple to add to your web application.