Monday, April 20, 2009 9:32 AM Kazi Manzur Rashid

AdRotator for ASP.NET MVC

I was looking  for an Ad Rotator for DotNetShoutout, as the sponsors are coming and most of them wants to show different images for the site and as well as in the feed. Certainly, I can use the built-in AdRotator control for the site, but it does not at all feels MVC-ish. So I decided to create a small class which mimics the same behavior as the original control.

namespace Kigg.Web
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Mvc;
    using System.Web.Routing;

    public class Ad
    {
        public string NavigateUrl
        {
            get;
            set;
        }

        public string Target
        {
            get;
            set;
        }

        public object LinkAttributes
        {
            get;
            set;
        }

        public string ImageUrl
        {
            get;
            set;
        }

        public string AlternateText
        {
            get;
            set;
        }

        public object ImageAttributes
        {
            get;
            set;
        }

        public string Keyword
        {
            get;
            set;
        }

        public int Impressions
        {
            get;
            set;
        }

        public static string Rotate(string keywordFilter, params Ad[] ads)
        {
            Ad ad = PickAd(keywordFilter, ads);

            string html = (ad == null) ? string.Empty : GenerateHtml(ad);

            return html;
        }

        public static string Rotate(params Ad[] ads)
        {
            return Rotate(null, ads);
        }

        private static Ad PickAd(string keywordFilter, params Ad[] ads)
        {
            Ad targetAd = null;

            IList<Ad> matchedAds = ads.Where(ad => string.Compare(ad.Keyword, keywordFilter, StringComparison.InvariantCultureIgnoreCase) == 0)
                                      .OrderBy(ad => ad.Impressions)
                                      .ToList();

            if (matchedAds.Count > 0)
            {
                int max = matchedAds.Sum(ad => ad.Impressions);
                int random = new Random().Next(max + 1);
                int runningTotal = 0;

                foreach(Ad ad in matchedAds)
                {
                    runningTotal += ad.Impressions;

                    if (random <= runningTotal)
                    {
                        targetAd = ad;
                        break;
                    }
                }

                if (targetAd == null)
                {
                    targetAd = matchedAds.Last();
                }
            }

            return targetAd;
        }

        private static string GenerateHtml(Ad ad)
        {
            Action<TagBuilder, object> merge = (builder, values) =>
                                               {
                                                   if (values != null)
                                                   {
                                                       builder.MergeAttributes(new RouteValueDictionary(values));
                                                   }
                                               };

            Action<TagBuilder, string, string> mergeIfNotBlank =    (builder, name, value) =>
                                                                    {
                                                                         if (!string.IsNullOrEmpty(value))
                                                                         {
                                                                             builder.MergeAttribute(name, value, true);
                                                                         }
                                                                    };

            TagBuilder imageBuilder = new TagBuilder("img");

            merge(imageBuilder, ad.ImageAttributes);
            mergeIfNotBlank(imageBuilder, "src", ad.ImageUrl);
            mergeIfNotBlank(imageBuilder, "alt", ad.AlternateText);

            if (!imageBuilder.Attributes.ContainsKey("alt"))
            {
                imageBuilder.Attributes.Add("alt", string.Empty);
            }

            TagBuilder linkBuilder = new TagBuilder("a");

            merge(linkBuilder, ad.LinkAttributes);
            mergeIfNotBlank(linkBuilder, "href", ad.NavigateUrl);
            mergeIfNotBlank(linkBuilder, "target", ad.Target);

            linkBuilder.InnerHtml = imageBuilder.ToString(TagRenderMode.SelfClosing);

            return linkBuilder.ToString();
        }
    }
}

And in the view I can use it like the following:

<%= Ad.Rotate(
                new Ad { NavigateUrl = "http://mysponsor1.com?tid=1", LinkAttributes = new { rel = "nofollow" }, ImageUrl = Url.Image("sponsors/mysponsor1/1.gif"), AlternateText = "My Cool Sponsor", ImageAttributes = new { style = "border:0"}, Impressions = 60 },
                new Ad { NavigateUrl = "http://mysponsor1.com?tid=2", LinkAttributes = new { rel = "nofollow" }, ImageUrl = Url.Image("sponsors/mysponsor1/2.gif"), AlternateText = "My Cool Sponsor", ImageAttributes = new { style = "border:0" }, Impressions = 40 },
                new Ad { NavigateUrl = "http://mysponsor1.com?tid=3", LinkAttributes = new { rel = "nofollow" }, ImageUrl = Url.Image("sponsors/mysponsor1/3.gif"), AlternateText = "My Cool Sponsor", ImageAttributes = new { style = "border:0"}, Impressions = 20 }
             )%>

It will randomly rotate based upon the impression value like the original control.  Though keyword filtering is not required for DotNetShoutout, but I have implemented it, maybe you can find it useful.

Shout it
Filed under: , , , ,

Comments

# re: AdRotator for ASP.NET MVC

Tuesday, April 21, 2009 6:57 AM by mosessaur

Did you submit this to KiGG project?

Really good job, and good implementation

# re: AdRotator for ASP.NET MVC

Tuesday, April 21, 2009 7:51 AM by Kazi Manzur Rashid

@mosessaur: Not yet, we do it soon.

# re: AdRotator for ASP.NET MVC

Tuesday, April 21, 2009 8:39 AM by Jones

Nice blog...

# re: AdRotator for ASP.NET MVC

Tuesday, April 21, 2009 1:42 PM by bengtbe

Good post! I would suggest using 3 extension methods in the GenerateHtml method, instead of the actions. Will improve readability, and they can be reused by other controls:

private static string GenerateHtml(Ad ad)  

{  

TagBuilder imageBuilder = new TagBuilder("img");  

imageBuilder.MergeAttributes(ad.ImageAttributes);  // 1

imageBuilder.MergeAttributeIfNotBlank("src", ad.ImageUrl);  // 2

imageBuilder.MergeAttributeWithDefaultIfBlank("alt", ad.AlternateText, "");  // 3

TagBuilder linkBuilder = new TagBuilder("a");  

linkBuilder.MergeAttributes(ad.LinkAttributes);  

linkBuilder.MergeAttributeIfNotBlank("href", ad.NavigateUrl);  

linkBuilder.MergeAttributeIfNotBlank("target", ad.Target);  

linkBuilder.InnerHtml = imageBuilder.ToString(TagRenderMode.SelfClosing);  

return linkBuilder.ToString();

}