Click here to Skip to main content
Click here to Skip to main content

CountryListBox ASP.NET Web Control, lists countries and automatically detects country of visitors.

, 11 Jan 2003
Rate this:
Please Sign up or sign in to vote.
A DropDownList derived control that lists all countries and performs an IP Address database lookup, to determine the country of visitors automatically.

Introduction

The purpose of this article is to show how to create a more specialized DropDownList web control - specifically one to be used for displaying Countries - suitable for use on ASP.NET Web Forms. The main advantage of this is (not only that it displays all countries, adding each one in HTML could be pretty tedious) but it will also automatically recognize the country that the visitor is from, well, the country the person is visiting the website from.

The web control uses a database from MaxMind, specifically its GeoIP Free Database. MaxMind also provide a C# class to access its database.

It's a very simple control, and the bulk of the work is performed by the ready-prepared CountryLookup class so the best way to go through it is provide an overview of how it works, and then the implementation details.

As ever, a live demo is available on my website.

How it works

Broadly, the CountryListBox web control derives from DropDownList and adds the contents of an ASCII text file into the CountryListBox's Items collection (a list of countries). MaxMind provides a CountryLookup class and within that class is an array of strings for each country - a utility program was created to dump the contents of the this array into an ASCII text file. Part of the array includes "N/A" and "Anonymous Proxy", these were removed from the text file. This text file is included in both the archives so you don't have to worry about re-creating this.

After the countries have been loaded a lookup is performed against the IP database to determine the location of the current visitor, this is then selected in the drop down list box - allowing users to correct it if it's wrong. Aside from that it behaves as any other DropDownList control would.

Now the technical details behind MaxMind's GeoIP database (not strictly necessary to understand since the C# class is provided by MaxMind).

MaxMind store IP address ranges, each of these records is also noted against a country. For example (in CSV format), "1029177344","1029439487","AU","Australia". The IP address is calculated as ipnum = 16777216*w + 65536*x + 256*y + z. For example, to compute the IP Number based on 24.24.24.24: 404232216 = 16777216*24 + 65536*24 + 256*24 + 24. This is all handled courtesy of the CountryLookup class -- which is available to download direct from MaxMind (but is included in the CountryListBox assembly).

Implementation details

The control is relatively simple in design, the majority of the work is performed in the OnInit override. The purpose of which is to add all the countries to the control's Items collection, and then select the one the visitor is from.

OnInit

Below is part of the code for the OnInit method. It includes code to determine whether the Application Cache should be used to store the Geo IP Database (this is set through the CountryListBox's CacheDatabase property. If it is to use the cache, and detects that the data is not already stored, it loads the file into a MemoryStream object (this is performed through the FileToMemory static method - this was added to the CountryLookup class by me). This MemoryStream object is then stored in the Application Cache.

// Check to see if the application cache should be used
if (useAppCache)
{
  // Check to see whether the IP Database is
  // already in the Application Cache
  if (Context.Cache.Get("GeoIPData") == null)
    // No, so store it as well as setting a dependency on the file
    Context.Cache.Insert("GeoIPData",
       CountryLookup.FileToMemory(
         ConfigurationSettings.AppSettings["GeoDatFile"]),
         new CacheDependency
           (ConfigurationSettings.AppSettings["GeoDatFile"]));

The LoadCountries method is then called to populate the Items collection and a lookup is performed, and the matching country selected.

    // Load the countries from the ASCII file into the control
    LoadCountries();

    // Perform the lookup using the MemoryStream taken from the Cache
    CountryLookup cl = new CountryLookup(
        ((MemoryStream)Context.Cache.Get("GeoIPData"))
        );

    // What country is the visitor from?
    string visitorCountry = cl.lookupCountryName(
        this.Page.Request.ServerVariables["REMOTE_ADDR"]
        );

    // Select the country in the control
    this.SelectedIndex = this.Items.IndexOf(
        new ListItem(visitorCountry,visitorCountry)
        );
}

CountryLookup changes

The CountryLookup class was provided by MaxMind, and so a few changes were necessary to enable the search to be performed against a database stored in memory. This turned out to be pretty easy thanks to .NET's stream infrastructure.

Deploying the demo

Within the demo is a test Web Form, the GeoIP database, the Countries text file and the CountryListBox assembly. The directory structure is ready to copy, but the contents of web.config will have to be changed to reflect the different paths. You're free to place these files anywhere (i.e. outside of the publicly accessible file structure), just ensure that the ASP.NET User account has the necessary access rights.

You'll have to do a few basic things to add the control to your page,

  1. Add the import statement to the top of the page:
    <%@ Register TagPrefix="etier" Namespace="Etier" 
                             Assembly="CountryListBox" %>
  2. Then add the tag to the page, it should support any of the standard DropDownList properties (although I haven't tested these thoroughly Smile | :) )
    <etier:CountryListBox
      Id="MyListBox"
         RunAt="server"
      CacheDatabase=true
      CacheCountries=true
    />

Performance

I performed some limited testing on the control to see how it performed when under load. I use Windows XP Professional as my development platform which has a restricted version of IIS - limited to 10 simultaneous connections. I used Microsoft Application Center Test to perform the testing, which involved loading the demonstration page as many times as possible over 5 minutes.

Non-cached version results

The graph below shows the results of the test using the standard control (without any caching). Each time a page is requested the GeoIP Database file is loaded and searched, the countries are also loaded from the text file. Below the graph are some basic statistics that were recorded during the test.

Average requests per second: 32.47
  Average time to first byte (msecs): 279.39 
  Average time to last byte (msecs): 279.55 
  
  Response Code: 403 - The server understood the request, but is refusing 
  to fulfill it. 
  Count: 5,429
  Percent (%): 55.73 
  
  
  Response Code: 200 - The request completed successfully. 
  Count: 4,313 
  Percent (%): 44.27

Since my local machine is not really designed to be a server (it's an Athlon XP 2000+ based machine, with 512MB of RAM but without any SCSI hard disks) it's fair to assume that a dedicated server would perform better. Despite this, the server could only sustain an average of 32 requests a second.

Cached results

To improve performance I used ASP.NET's Application Cache to store both the GeoIP Database and the countries. The option to use both of these can be set through the CacheDatabase and CacheCountries properties. I then ran the exam same test script, the graph of the results below shows quite a dramatic difference (far greater than I expected). Again, some basic statistics are included below.

Average requests per second: 146.01 
  Average time to first byte (msecs): 43.48 
  Average time to last byte (msecs): 43.76 
  
  
  Response Code: 403 - The server understood the request, but is refusing 
  to fulfill it. 
  Count: 6 
  Percent (%): 0.01 
  
  
  Response Code: 200 - The request completed successfully. 
  Count: 43,797 
  Percent (%): 99.99

As a result of caching the number of requests that can be handled per second has increased from 32 to 146, an increase of around 350%.

Conclusion

Thanks has to go to Per Soderlind for writing an article that first told me about MaxMind's GeoIP database. Thanks must also go to MaxMind for providing the database free of charge. It appears that its free of charge to commercial applications also -- but commercial services are also offered that include details on region, state and even NetBlock owners!

It's an extremely simple control to create, but one that is extremely useful! It should hopefully prevent quite so many people wrongly filling out forms. Once again, feel free to E-mail me if you have any comments or questions, alternatively post a message below.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Paul Ingles
Web Developer
United Kingdom United Kingdom
I graduated with a first class BSc honours degree in a Computer Science/Management Science hybrid Smile | :) from Loughborough University. I live in London and currently work as a .NET Developer in central London.
 
I'm also currently blogging my progress at further developing my articles into commercial components. Read my blog here.
 
I've also recently started dabbling at digital photography, and digital re-touching, and developing small simple multiplayer games in Flash.

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermsg2swaroop@gmail.com12-Oct-10 21:26 
GeneralStatus PinmemberRalph Mullenders23-Jul-07 6:09 
GeneralUnavailabilty of demo PinmemberPaul Ingles8-Feb-04 13:05 
GeneralGeoCoding PinsussAnonymous19-Dec-03 2:28 
GeneralIPAddress.Address update PinmemberNoFrills9-Dec-03 14:09 
GeneralDAT file Pinmemberwilson7810-Sep-03 19:35 
GeneralRe: DAT file PinmemberPaul Ingles10-Sep-03 23:00 
GeneralRe: DAT file Pinmemberwilson7810-Sep-03 23:14 
Generalin VB.net Pinmemberpolgnz5-Jun-03 20:10 
GeneralExpose Country code as Key PinmemberJonTampa8-Feb-03 16:01 
GeneralRe: Expose Country code as Key PinmemberPaul Ingles7-Apr-03 14:15 
GeneralUpdate... PinmemberIkke7-Dec-02 9:00 
GeneralRe: Update... PinmemberPaul Ingles7-Dec-02 12:39 
GeneralRe: Update... PinmemberIkke8-Dec-02 2:04 
QuestionPerformance? PinadminChris Maunder3-Dec-02 13:27 
AnswerRe: Performance? PinmemberPaul Ingles4-Dec-02 4:06 
AnswerRe: Performance? - Test Results PinmemberPaul Ingles6-Dec-02 2:44 
GeneralRe: Performance? - Test Results PinadminChris Maunder6-Dec-02 7:19 
GeneralRe: Performance? - Test Results PinmemberRamon Smits4-Jul-03 8:30 
GeneralRe: Performance? - Test Results PinmemberPaul Ingles7-Jul-03 7:08 
GeneralRe: Performance? - Test Results Pinmembertsmyrnio7-Oct-04 20:29 
GeneralToolbar (OT) PinmemberKannan Kalyanaraman2-Dec-02 18:55 
GeneralRe: Toolbar (OT) PinsitebuilderUwe Keim2-Dec-02 20:33 
GeneralRe: Toolbar (OT) PinmemberPaul Ingles3-Dec-02 2:11 
Generalduplicate article Pinmembersheebz2-Dec-02 10:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 12 Jan 2003
Article Copyright 2002 by Paul Ingles
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid