Howto Create a Hit Counter Using a Database in ASP.NET 1.x with C#

If you have a live Web site on the World Wide Web, you may be interested in how many people are visiting your site. You can of course analyze the log files of your Web server but that information is usually difficult to read. The log files contain information for each and every request a visitor has made to your site, including resources like images, Flash movies and so on. This makes it near impossible to extract information about individual users. It would be a lot easier if you could count the number of individual users that have visited you since you started your site. It would also be useful if you could see the number of users that are currently browsing your site.

This article will show you how to accomplish these two tasks by storing the hit counters in shared variables in the Global class and in a database using code in the Global.asax file. The counters in the shared variables are used to display them on a page in your Web site; either as a counter so your visitors can see it as well, or somewhere on a page in your Admin section, so only you have access to them. By writing the counters to a database you can maintain their value even when you restart the Web server, while you still have a fast and scalable solution.

This article extends the ideas from two previous articles where the values of the counters were just stored in static variables in the Global class and in a text file.

There are also Classic ASP and VB.NET versions of this article available.

Prerequisites

The code in this article uses Sessions in ASP.NET, so you'll need to have them enabled on your server, by configuring the <sessionState> element in the Web.config file. Refer to the References section at the end of this article for more details.
You'll also need to have access to a file called Global.asax in the root of your site. If you run your own Web server, this is not a problem; you can simply create the file yourself. If you are using an ISP, you'll need to check with them if they support the use of the Global.asax file as, unfortunately, not all ISPs allow this.

You'll also need to be able to write to and read from a database from within the Global.asax file. This means that the account your Web site is running under (usually, this is the ASPNET account) needs sufficient permissions to write to the database file and the folder it resides in.

Finally, this article assumes you're using Visual Studio .NET 2002 or 2003, as it shows code and ASPX pages using the Code Behind model. Don't worry if you don't have Visual Studio .NET; it should be relatively easy to use the code in your own Code Editor like Macromedia Dreamweaver MX or Notepad.
 

Counting Users

Just as in the article where the counters were stored in the Global class, you can make use of the Session_Start event, defined in the Global.asax file to keep track of your users. This event is fired whenever a user requests the first page in your Web site. This way, you have the ability to count each unique visitor only once during their visit. As long as the Session remains active on the server, the user won't be counted again. After the Session has timed out (it will automatically time out after a certain interval when no new pages have been requested) or has been explicitly ended, a request to a page will create a new Session, Session_Start will fire and the user will be counted again.

To keep track of the total number of users that have visited your site since you started the Web server, you can increase a counter for each request a user makes. Let's call this counter totalNumberOfUsers. You can store that counter in a static variable in the Global class, so you can retrieve and display it on other pages in your site. You can also create a second counter, called currentNumberOfUsers for example, that counts the number of active Sessions on your server. Just as with totalNumberOfUsers, you increase the value of this counter whenever a new Session is started. However, you should decrease its value again when the Session ends. so you can keep track of the number of users that are currently visiting your site.

Besides storing these values in the Global class, I'll show you how to save them in a database, so the value for the counter is preserved, even when the Web server is stopped and restarted. When your server is restarted, code in the Global.asax file will read in the old value from the database, so your counter will continue with the value it had before the restart. Don't underestimate the problem of restarting your Web server. Even if you have your stable Windows 2003 box running for months without a reboot, the new IIS Process Recycling feature may restart your Web application every day or even every few hours.

This example uses a Microsoft Access database that is called Website.mdb. The database has just one single table called Counters. This table has two columns called CounterType and NumberOfHits. The CounterType column defines the type of counter you want to store in the database. The table looks as follows:

 

Tabel Counters that can store various types of Hit Counters
Figure 1 - The Counters Table

In this example, the type of counter will be UserCounter, to indicate you are counting users. You can expand on this example and count individual pages, for example. In that case, you could store the page name in the CounterType column to indicate which page you are tracking.
The NumberOfHits column will hold the number of hits for the counter. In this example, it stores the number of visitors to your site.

The code assumes that you have saved this database (which is available in the code download for this article) in a folder called C:\Databases. If you decide to store your database at a different location, make sure you adjust the path in the code that references the database. You'll also need to make sure that the ASPNET account has sufficient permissions to both the database and the folder Databases.

Once you have set up the database, you should make sure you have a file called Global.asax (note that the extension is different from ordinary aspx pages) in the root of your Web site. Usually, when you create a new Visual Studio ASP.NET Web Application, the Global.asax file is already there, and the skeleton for important events like Session_Start and Session_End are already present in its Code Behind file.

If you don't have the file yet, open the Visual Studio .NET Solution Explorer (Ctrl+Alt+L), right-click your Web project and choose Add | Add New Item... from the context menu. Scroll down the list with Web Project Items until you see Global Application Class. Alternatively, expand Web Project Items and then click Utility to limit the list of Web items. Select the Global Application Class and click Open to add the Global.asax file to your Web site project.

The page will open in Design View so you'll need to click the link "click here to switch to code view" to view the Code Behind file for the Global.asax file. You'll see some default using statements, followed by the definition for the Global class. I'll show you how to expand this class in the remainder of this article by adding a few private variables for the two hit counters. These private variables will then be made accessible through public properties. Using properties instead of public fields helps keeping your code cleaner and more stable. Calling code is not allowed to just arbitrarily change the field's value; it has to change the value through a public Set method. In this example, you'll make the code even a bit more safe by removing the Set method altogether. This makes it impossible for calling code to change the value of the counter; all it can do is read its value.
 

Modifying Global.asax


 

  1. Locate the code that starts with public class Global : System.Web.HttpApplication and add the following shaded lines of code:
namespace HitCounters
{
  /// <summary>
  /// Summary description for Global.
  /// </summary>
  public class Global : System.Web.HttpApplication
  {
 
    private static int totalNumberOfUsers = 0;
    private static int currentNumberOfUsers = 0;

    /// <summary>
    /// Required designer variable.
  1. The code that writes the counters to the database, makes use of objects like the OleDbConnection, located in System.Data.OleDb namespace, so you'll need to add a reference to this namespace by adding a using statement, at the top of the page somewhere below the other using statements:
using System.ComponentModel;
using System.Web;
using System.Web.SessionState;
 
using System.Data.OleDb;
 
  1. The next step is to add code to the Session_Start event. This event will fire once for each user when they request the first page in your Web application, so this place is perfect for your hit counters. Inside this event, the values of the two hit counters are increased; one for the total number of users and one for the current number of users. Once the values are increased, the value of totalNumberOfUsers will be written to the database as well.

    Locate the skeleton for the Session_Start event and add the following code:
protected void Session_Start(Object sender, EventArgs e)
{
 
  // Increase the two counters.
  totalNumberOfUsers += 1;
  currentNumberOfUsers += 1;

  // Save the Total Number of Users to the database.
  string connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;
                              Data Source=C:\Databases\Website.mdb;
                              User ID=Admin;Password=";

  string sql = @"UPDATE Counters SET NumberOfHits = "
                    + totalNumberOfUsers + " WHERE CounterType = 'UserCounter'";

  OleDbConnection conn = new OleDbConnection(connectionString);

  OleDbCommand cmd = new OleDbCommand(sql, conn);

  // Open the connection, and execute the SQL statement.
  cmd.Connection.Open();
  cmd.ExecuteNonQuery();
  cmd.Connection.Close();
}
  1. Just as with the Session_Start event, you'll need to write some code for the Session_End event as well. However, instead of increasing the counters, you should decrease the counter for the current number of users only. This means that whenever a user Session times out (usually 20 minutes after they requested their last page), the counter will be decreased, so it accurately holds the number of current users on your site. You should leave the counter for the total number of users untouched.
    Locate the Session_End event, and add this line of code:
protected void Session_End(Object sender, EventArgs e)
{
  currentNumberOfUsers -= 1;
 
}

How It Works

The code that runs in the Session_Start event performs a few important steps; first of all, the values for the two hit counters are increased. Next, a connection string and a SQL statement are created. The connection string points to the database Website.mdb located at C:\Databases.

The SQL statement is a simple UPDATE statement that will update the value for the NumberOfHits column. You need to make sure that just the UserCounter record is updated, and the WHERE clause in the SQL statement takes care of that. This WHERE clause will make sure that only the right record is modified.
Notice that just the value of the TotalNumberOfUsers counter is saved. There is no need to store the current number of users; after all, when the Web server is restarted, there are no current users yet.

Next the connection object and a command object are constructed. I am using an OleDb connection in this example because a Microsoft Access database is used. If you're using SQL Server or another type of database, you'll need to construct a different kind of connection object, like a SqlConnection. The command object is used to send the SQL statement to the database over the open connection.

Finally, the connection is opened, the command executed and the connection is closed again.

The code that runs in the Session_End event simply decreases the value of the current number of users. There is no need to touch the counter for the total number of users.

Making Your Counters Accessible by Other Pages

Since the hit counters are stored in the Global class, you need some way to get them out of there, so you can display them on a management page for example. The easiest way to do this, is to create two public properties. Because the Global class is in many respects just an ordinary class, it is easy to add public properties to it.

To add the properties, locate the skeleton for the Application_End event, and add the following code just below it:

 

protected void Application_End(Object sender, EventArgs e)
{

}

 
public static int TotalNumberOfUsers
{
  get
  {
    return totalNumberOfUsers;
  }
}

public static int CurrentNumberOfUsers
{
  get
  {
    return currentNumberOfUsers;
  }
}
 

With these two properties in place, your calling code is now able to access the values of your hit counters. For example, to retrieve the number of users browsing your site right now, you can use this code: HitCounters.Global.CurrentNumberOfUsers where HitCounters is the default namespace for your Web application as defined on the Property Pages for the Web project in Visual Studio .NET. You also saw this namespace at the top of the Code Behind file for the Global class.
 

Reading the Counter When the Web Server Starts

As long as the Web server keeps running, this code will run fine. For each new Session that is created, the counters are increased by one and the value of totalNumberOfUsers is written to the file. If, however, the Web server stops unexpectedly, or your restart or reboot the Web server yourself, the values for totalNumberOfUsers and currentNumberOfUsers are lost. But because the value for totalNumberOfUsers has also been saved in the database, it's easy to retrieve the counter again when the Web server starts. To read in the value from the database, you'll need to add some code to the Application_Start event that is also defined in the Global.asax file:

 

protected void Application_Start(Object sender, EventArgs e)
{
  // Get the Total Number of Users from the database.
  string connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;
                            Data Source=C:\Databases\Website.mdb;
                            User ID=Admin;Password=";

  string sql = @"SELECT NumberOfHits FROM Counters
                WHERE CounterType = 'UserCounter'";

  OleDbConnection conn = new OleDbConnection(connectionString);

  OleDbCommand cmd = new OleDbCommand(sql, conn);

   // Open the connection, and execute the SQL statement.
  cmd.Connection.Open();
  OleDbDataReader reader = cmd.ExecuteReader();
  if (reader.Read())
  {
    totalNumberOfUsers = reader.GetInt32(0);
  }
  else
  {
    totalNumberOfUsers = 0;
  }

  // Close the reader and the connection
  reader.Close();
  cmd.Connection.Close();
 
}

This code is more or less the same as the code that was used to write the value to the database. What's different here is that a SELECT statement is used to retrieve the value for the hit counter from the database. The same WHERE clause is used to make sure you get the value for the right counter.

What's also different is the use of an OleDbDataReader to retrieve the value from the database. If the Read method returns true, it means that the requested counter has been found. In that case, the value from the NumberOfHits column is stored in the private variable called totalNumberOfUsers. The GetInt32 method makes sure the value is retrieved as an int. If the counter is not found, the private variable is reset to 0. (Note that it is unlikely that the record is not found, as it is defined in the database. If you ever delete this record, the UPDATE statement that sets the counter in the Session_Start will not run correctly, because it expects the record to be present).

At the end, the OleDbDataReader and the Connection are closed. This is good practice, as leaving DataReaders and Connections open can severely limit the number of concurrent users your site can serve.

Testing it Out

To test out your hit counters, create a new Web form and call it HitCounter.aspx. You can save the form anywhere in your site. In Design View, add two labels to the page and call them lblTotalNumberOfUsers and lblCurrentNumberOfUsers respectively. Add some descriptive text before the labels, so it's easy to see what value each label will display. You should end up with something similar to this in Code View:

 

<body>
  <form id="frmHitCounter" method="post" runat="server">
    Total number of users since the Web server started:
    <asp:label id="lblTotalNumberOfUsers" runat="server"></asp:label><br />
    Current number of users browsing the site:
    <asp:label id="lblCurrentNumberOfUsers" runat="server"></asp:label><br />
  </form>
</body>

Press F7 to view the Code Behind for the hit counter page, and add the following code to the Page_Load event:

 

private void Page_Load(object sender, System.EventArgs e)
{
  int currentNumberOfUsers = HitCounters.Global.CurrentNumberOfUsers;
  int totalNumberOfUsers = HitCounters.Global.TotalNumberOfUsers;
  lblCurrentNumberOfUsers.Text = currentNumberOfUsers.ToString();
  lblTotalNumberOfUsers.Text = totalNumberOfUsers.ToString();
 
}

The first two lines of code retrieve the total number of visitors and the current number of visitors from the Global class. The next two lines simply display the values for the counters on the appropriate labels on the page.

To test it out, save the page and view it in your browser. You'll see there is one current user. Also, note that the total number of users is 1. Open another browser (don't use Ctrl+N, but start a fresh instance or use an entirely different brand of browser) and open the counter page. You'll see there are two current users, and two users in total.

Next, restart your Web server. If you are using IIS, you can choose Restart IIS... from the All Tasks context menu for your Web server in the IIS Management Console. Alternatively, you can recompile your application in Visual Studio .NET. Whenever you do a recompile, the Web application will restart automatically.
Open HitCounter.aspx again. You'll see that currently you're the only user browsing the site, but the total number of users has maintained its value.
 

Summary

This article demonstrated how to create a hit counter that keeps track of the current and total number of users to your site. It stores these counters in static variables in the Global class so they are available in each page in your Web site. It also stores the value for the total number of users in a database so its value won't be lost when the Web server restarts, either unexpectedly, or on purpose.

By using a database, you have created a solution that can easily be scaled to lots of users. If you have a real busy Web site, you can change the code from this article so it uses a real Database Management System, like Microsoft SQL Server or Oracle. These kind of work beasts can easily serve thousands and thousands of users a day allowing for lots of concurrent users.

Related Articles

References

Download Files


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Wednesday, April 06, 2005 3:11:33 AM Jester said:
Im curious if you would have a sql script for this hit counter so that I could try it out.
On Wednesday, April 06, 2005 5:53:26 AM Imar Spaanjaars said:
Hi Jester,

I am not quite sure what Sql script you're referring to. The code download for this article comes with an Access database that has the required table. Also, the code contains the Sql statements that update this hit counter.
What Sql script do you need?

Imar
On Wednesday, April 20, 2005 3:46:43 PM Sandhya said:
Is there a way to keep track of hits for every page. For instance if my website has 20 pages can i keep track of each url and the hits for the URL.
On Wednesday, April 20, 2005 3:54:16 PM Imar Spaanjaars said:
Hi Sandhya,

Check out the comments at the bottom of this article: http://imar.spaanjaars.com/QuickDocID.aspx?QUICKDOC=165

Josh asked pretty much the same question, and I provided a solution for classic ASP. However, the same principle applies to .NET.

Let me know if this doesn't help...

Imar
On Wednesday, April 20, 2005 5:53:32 PM Jester said:
An sql script, one that is entered in sql query analyzer to create the sql database for me.

Thanks for replying back
On Wednesday, April 20, 2005 6:59:42 PM Imar Spaanjaars said:
It's probably the easest table you can imagine....

CREATE TABLE [dbo].[Counters] (
    [CounterType] [nvarchar] (200) NOT NULL ,
    [NumberOfHits] [bigint] NOT NULL
) ON [PRIMARY]
GO

If you want to make CounterType the primary key:

ALTER TABLE [dbo].[Counters] WITH NOCHECK ADD
    CONSTRAINT [PK_Counters] PRIMARY KEY  CLUSTERED
    (
        [CounterType]
    )  ON [PRIMARY]
GO

And if you want to initialize each NumberOfHits to 0:

ALTER TABLE [dbo].[Counters] ADD
    CONSTRAINT [DF_Counters_NumberOfHits] DEFAULT (0) FOR [NumberOfHits]
GO
On Saturday, July 22, 2006 7:56:06 AM manish said:
this helped me much.
but it would be better if i could track for all the pages. how much user has visited this site.
thanx for help

                    manish
On Sunday, July 23, 2006 11:18:16 AM Imar Spaanjaars said:
Hi there,

I don't understand what you're asking here. Doesn't this code tell you how many users visited your site??

Can you explain what you want to do?

if you need more detailed information about the site usage, you can also look into tools that analyze the web server logs, like LiveStats from http://www.deepmetrix.com

Cheers,

Imar
On Friday, August 04, 2006 6:49:48 AM sudhir bharti said:
Is there a way to keep track of hits for every page. For instance if my website has 20 pages can i keep track of each url and the hits for the URL.
On Saturday, August 05, 2006 8:56:51 AM Imar Spaanjaars said:
Hi sudhir,

Yeah, that's possible too. You could retrieve the name of the current file, for example with Request.CurrentExecutionFilePath (http://msdn2.microsoft.com/en-us/library/system.web.httprequest.currentexecutionfilepath.aspx) and use that name as the countertype. So, instead of sending UserCounter to the database, you send the name of the page.

Does that help?

Imar
On Wednesday, August 30, 2006 5:52:52 PM Sarp Akal said:
It is great article, well designed for a starter to understand. Thank you.
I like reading it and wish to continue with 'using XML file' version.
When I was reading I also expect a brief comparison of the methods on performance.
On Wednesday, August 30, 2006 6:07:07 PM Imar Spaanjaars said:
Hi Sarp,

Performance depends on many factors. But obviously, a database will be more reliable and scalable than a text file. But, when exactly you reach the limit of the text file pretty much depends on how your server is used.

I won't do an XML version of this artile, because it'll be alsmost identical to the text file version.

Cheers,

Imar
On Thursday, August 31, 2006 9:32:24 PM Sarp Akal said:
Hi Imar,

At my initial trial of creating Global.asax, I am confised why VS2005 do not create Global.aspx.cs as codebehind automatically even general setup for codebehind. Any idea why? So, I could not define HitCounters namespace in Global.asax and HitCounters.Global.TotalNumberOfUsers gave error.

Without creating Global.aspx.cs manually, is there a way to use Global Class in source code, I mean any way to define namespace in Global.aspx or  adding using system... namespace to the codes to use Global.TotalNumberOfUsers?

Sarp
On Friday, September 15, 2006 1:54:05 PM Imar Spaanjaars said:
Hi Sarp,

Yes, there is. Add a file called MyGlobals.cs to the App_Code folder. Add a class definition to this file similar to this:

public class MyGlobals : HttpApplication
{

}

This class will be your Global class. Then change your global.asax file to this:

[%@ Application Language="C#" Inherits="MyGlobals" %]

(Replace the [ and ] with regular brackets)

Now you can add the code and handlers like Application_Start to the MyGlobals class in the App_Code folder.

Cheers,

Imar
On Saturday, April 28, 2007 12:53:27 PM Nazi said:
Hi,
This code works fine for me. However if I view that page under mozilla firefox this deosnt work correctly. It will display double the amount of users that are online evertime the page is refreshed, which is false...

thanks,
Nazi
On Saturday, April 28, 2007 12:58:57 PM Imar Spaanjaars said:
Hi there,

Since this is server based code, the actual browser doesn't have much to do with the total count of on-line users. You should see the same amount in all browsers.

However, it may the case that your browser starts additional sessions on the server, resulting in a larger number of users. This may depend on the browser, and the way it accepts and handles cookies.

Cheers,

Imar
On Saturday, April 28, 2007 1:21:49 PM Nazi said:
Thanks for your quick reply

be happy.
On Thursday, May 03, 2007 7:12:14 AM peter said:
It's a great article, but I just want to make sure that it overcomes the lost update problem. i.e. the session start event is not "atomic" (non interrupted) peace of code. so if two sessions started at nearly the exact same time, there might be a chance for an increment to be lost.
and if so may the use of the "lock" statement be useful?
On Thursday, May 03, 2007 4:33:45 PM Imar Spaanjaars said:
Hi peter,

Yes, you're correct. You can use lock statements, or simply call Application.Lock and Application.UnLock to prevent the problem.

HtH,

Imar
On Wednesday, July 18, 2007 8:16:13 AM duyvu said:
I was down your code and build in vs .Net 05 it run fine, but when I put project into Inetpub and run localhost then Browser notice error

Compiler Error Message: CS0234: The type or namespace name 'Global' does not exist in the namespace 'HitCounters' (are you missing an assembly reference?)
Line 22:         private void Page_Load(object sender, System.EventArgs e)
Line 23:         {
Line 24:             int currentNumberOfUsers = HitCounters.Global.CurrentNumberOfUsers;
Line 25:             int totalNumberOfUsers = HitCounters.Global.TotalNumberOfUsers;
Line 26:            
Source File: c:\Inetpub\wwwroot\WebSite1\HitCounter.aspx.cs

I hope you can show me that error.
Thanks a lot!!!
On Wednesday, July 18, 2007 8:21:36 AM Imar Spaanjaars said:
Hi duyvu,

Please read the comments under this article. This "issue" in VS 2005 has been reported before.

Cheers,

Imar
On Wednesday, July 18, 2007 8:47:34 AM duyvu said:
Im a recruit of .Net and this website, I dont know have anyone like to me about this error or not. So, you can show me the thread discuss about my problem or not? Or you can show to me via send email.
Thank you very much.
Have a good day! Imar
On Wednesday, July 18, 2007 8:53:56 AM Imar Spaanjaars said:
Hi duyvu,

Search this article for 2005 and you'll find some replies. In addition, take a look here:

http://rossnelson.blogspot.com/2005/11/fixing-globalasax-in-aspnet-20.html

Imar
On Tuesday, July 01, 2008 6:39:23 AM santosh Parida said:
Hi,

    I am using Asp.net 2.0 and using vb.net. But I am not getting the code behind file for Gobal.asax. Where I will write the Class ?

    Waiting for your soon Reply...............
On Tuesday, July 01, 2008 3:07:06 PM Imar Spaanjaars said:
Hi santosh,

It depends on the type of project. You can write it in-line in Global.asax with a Web Site Project, or in the Code Behind with a Web Application Project.

Did you read the link I posted just above this comment?

Cheers,

Imar
On Wednesday, July 23, 2008 8:56:03 AM dhanya said:
i need to make hitcount for all pages in my website..how to put current page name to CounterType
On Wednesday, July 23, 2008 10:26:27 AM Imar Spaanjaars said:
Hi dhanya,

Check out the comments at the bottom of this article: http://imar.spaanjaars.com/QuickDocID.aspx?QUICKDOC=165

Josh asked pretty much the same question, and I provided a solution for classic ASP. However, the same principle applies to .NET.

Let me know if this doesn't help...

Imar
On Wednesday, July 23, 2008 11:49:04 AM santosh Parida said:
Dear Imar,

         It is working perfectly. Thanks a lot.
On Sunday, November 30, 2008 7:19:11 AM Hariom Thakur said:
nice article keep it up dude.
On Friday, December 19, 2008 9:22:18 PM alireza said:
i am using asp2 and c# and run this progeram
but run thats show error
int currentNumberOfUsers = Spaanjaars.Imar.Web.Global.CurrentNumberOfUsers;
        int totalNumberOfUsers = Spaanjaars.Imar.Web.Global.TotalNumberOfUsers;
        
The name 'Spaanjaars' does not exist in the current context
C:\New Folder\WebSite14\HitCounter.aspx.cs 21 36 C:\New Folder\WebSite14\
pls help me .
On Friday, December 19, 2008 9:39:00 PM Imar Spaanjaars said:
Hi alireza,

Spaanjaars.Imar.Web is the namespace of *my* application. Maybe you have a different Global class and namespace?

Also, note that this article was written for ASP.NET 1.1; you can't run it out of the box on later versions of Visual Studio without making some changes to the Global.asax file.

Cheers,

Imar
On Friday, May 15, 2009 1:25:21 PM Rachel said:
Hi Imar:
Is there a way to use this code without an Access database? I don't have access. I'd like to add a web counter to my web page which uses sqlexpress.

Rachel
On Friday, May 15, 2009 8:50:50 PM Imar Spaanjaars said:
Hi Rachel,

Yeah, you can certainly do that. Most of it should work as-is on SQL. But you need to change:

1. The connection string (which should really be stored in the web.config and not be hard coded in the code)

2. Change all OleDb* occurrences in the code to Sql.

3. Import the System.Data.SqlClient namespace.

If you make it work, I'll be happy to host your SQL version here if you're willing to share.

Cheers.

Imar
On Friday, May 15, 2009 11:54:44 PM rachel said:
Hi Imar:
I was going to try it but I got stuck right off. I guess you can't add a 2nd global.asax file since it doesn't appear in my choices when I tried to add it. I have a Global.asax but it doesn't have a separate code-behind file (just one .asax file).

Also it doesn't have some of the lines you say to add. It has only the first line, " <%@ Application Language="C#" %> "

and then some script which your website won't let me post here.

So can I add those lines to this file?
Rachel
On Saturday, May 16, 2009 12:15:14 AM Rachel said:
oh, I just saw that I also have a "Globals.cs" file under my App_code but its already using a namespace:
namespace MB.mywebsitename  for my themes.
On Saturday, May 16, 2009 1:40:51 AM Rachel said:
I just added another namespace to the global.cs file and added a table "Counters", to my mdf database. But How do i set the type to   "UserCounter" when creating my table? It doesn't have that type in the options to select. I used nvarchar(MAX) because I didn't know what else to use .

string sql = @"SELECT NumberOfHits FROM Counters  
WHERE CounterType = 'UserCounter'";
On Saturday, May 16, 2009 4:55:07 AM Imar Spaanjaars said:
Hi Rachel,

UserCounter is not a data type. The type of the column CounterType is text (e.g. something like a varchar(20) or nvarchar(20)) and simply contains the "text" "UserCounter". I did it like that so I can have multiple counters in the same database table.

Also, please note this article is from 2004 and written against .NET 1.1. You can't use the project / solution, only the code. If I were you, I'd create a Global.asax in my site (you're right you can only have one) or use your existing one and then copy the code from my example into your Global file at the appropriate locations.

Cheers,

Imar
On Saturday, May 16, 2009 10:14:24 PM rachel said:
Hi Imar:
Your post form here is very temperamental. It keeps giving erros when I try to post. Here goes another try:

What I'm not clear on now is the Global.asax.cs file. My global file doesn't have a separate code behind file like yours. Its just one page. (I think you have to make a new web "project" rather than "new website" to get that??)

My file only has some  "(script runat EQUAL "server")" stuff. When I try to add things, it won't accept it. I can't paste it here because your webpage won't let me -  but when I paste the using statements at top of page such as using system; etc., it all stays in black color. And when I pasted the code from your example also stays in black and gives a ton of errors when I try to build it.
Is there a way to get my global.asax file changed??
On Monday, May 18, 2009 2:06:42 PM Rachel said:
Hi Imar:
Never mind my comments above. I think there are other issues with conflicts with my set up that are interfering with this. I finally manually created a global file but my counters stay at 0 no matter what I do. I went with another counter.
thanks for you help anyway. Rachel
On Sunday, May 24, 2009 9:43:29 AM Imar Spaanjaars said:
Hi Rachel,

It's not so temperamental; it just doesn't allow you to post anything that looks like HTML or XML which includes the script element. I was hoping that the bold part of the error message you got a few times would make that clear ;-)

Imar
On Sunday, May 24, 2009 3:20:49 PM rachel said:
Hi Imar:
Yes, I realized it was certain characters that it was rejecting but even when I tried disguising it, it wouldn't let me post and it became exasperating. I didn't mean to sound like I was insulting your forms. :-)

Do you think you'll ever create a web counter control for c# that doesn't use access? (i hope - i hope). I've got a web counter working now but it doesn't do unique visitors. I wish I could find a control I could just drop on each page I wanted a count kept. I would pay for one if I could find one - but I can't.

Rachel
On Monday, May 25, 2009 5:02:41 PM Imar Spaanjaars said:
Hi rachel,

No worries; I checked with the forms and they were not insulted ;-)

No plans to rewrite the counter any time soon. Like I said, it's not too difficult to convert it, but I am quite busy writing other stuff.

If you're serious about payment, send me an e-mail with a list of stuff you need developed for this feature and an estimation of what you're willing to pay and I can see if I can take it to my company instead.

Cheers,

Imar
On Monday, November 09, 2009 10:02:09 AM lpandiyan said:
how to get decrease value in one table to get increase in another table in asp.net (back end in sql)
On Monday, November 09, 2009 10:47:31 AM Imar Spaanjaars said:
Hi lpandiyan,

I don't understand what you're asking. I also don't see how this is related to the concept of a hit counter. Do you?

Imar
On Thursday, November 12, 2009 8:34:40 AM Pavan sahu said:
Hi,
I have added MyGlobal.cs in App_Code folder but when I run my website I am getting an error  like 'Cannot load type 'MyGlobal' which is the one which I gave in Inherits value. please help me out.
On Thursday, November 12, 2009 6:26:29 PM Prashant said:
Why doesn't the downloadable source code run and show the no. of users in VS 2008? I mean to say the pages run but never show any numbers!!
On Friday, November 13, 2009 8:11:16 AM Imar Spaanjaars said:
Hi Pavan  and Prashant,

This code is for .NET 1.x and VS 2003, and won't work in VS 2008 without modification because there were changes in the way sites are set up and compiled between these versions.

Cheers,

Imar
On Tuesday, April 06, 2010 9:59:15 AM nayer said:
hi,I can not see  Counter ,I just see

Total number of users since the Web server started:
Current number of users browsing the site:


when  Run  Program.
Please Hellp me,Thanks
On Tuesday, April 06, 2010 10:02:50 AM Imar Spaanjaars said:
Hi nayer,

Check out my last comment on this article: this code is for .NET 1.x only. What version are you using?

Imar
On Sunday, April 18, 2010 9:28:40 AM Vishal Malhotra said:
hi....I am new in development field.Can any one tell me complete code making hit counter using asp.net 2.0 with database sql server 2005.
On Sunday, April 18, 2010 9:54:03 AM Imar Spaanjaars said:
For anyone interested in a .NET 3.5 solution (for Visual Studio 2008): I just uploaded a VS 2008 version of the code. Check out the Downloads section near the bottom of this article.

Cheers,

Imar
On Tuesday, February 15, 2011 6:54:55 AM alireza said:
Hi . I am beginner in development,i used this code to put my code, and change path databse

(@"Provider=Microsoft.Jet.OLEDB.4.0;
  Data Source=..\App_Data\bashiri.mdb;
User ID=Admin;Password=";)

But when I run my web site i am getting message :

please help me.
On Tuesday, February 15, 2011 8:12:37 AM Imar Spaanjaars said:
Hi there,

Have you tried assigning a full path (e.g. C:\PathToYourSite\App_Data\bashiri.mdb) instead of a relative path?

Otherwise, try posting this on a forum such as this one: http://p2p.wrox.com/index.php?referrerid=385 and be sure to describe your site's structure and the exact error message you're getting.

Cheers,

Imar
On Tuesday, February 15, 2011 9:40:43 AM alireza said:
Hi . I am beginner in development,I used this code to put my code, and change path databse .

(@"Provider=Microsoft.Jet.OLEDB.4.0;
  Data Source=..\App_Data\bashiri.mdb;
User ID=Admin;Password=";)

But when I run my web site i am getting message :

'C:\Databases\Website.mdb' is not a valid path.  Make sure that the path name is spelled correctly and that you are connected to the server on which the file resides.!!!!!!!

While I changed database path.

Please infor me where shuld i change ?


On Tuesday, February 15, 2011 9:48:56 AM Imar Spaanjaars said:
And what are the answers to the suggestions made by the error message:

Make sure that the path name is spelled correctly and that you are connected to the server on which the file resides

If that doesn't help, take another look at my previous reply and recommendation to post this on a forum *with detailed information*.

Cheers,

Imar
On Tuesday, February 15, 2011 11:05:51 AM alireza said:
How can i change the Database Path in this code?
On Tuesday, February 15, 2011 11:09:06 AM Imar Spaanjaars said:
Do you even read my replies to your questions?

Imar
On Tuesday, February 15, 2011 5:12:26 PM alireza said:
?Except Global.asax.cs file did u arrange database address

(private string _connectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;
   Data Source='C:\Databases\Website.mdb' ;
User ID=Admin;Password=";)

in another file or not?
I think the database file path in the reference file (HitCounters.dll) is located.
On Tuesday, February 15, 2011 5:49:35 PM Imar Spaanjaars said:
No, it's only defined where you see it. You can use Visual Studio's Search in Files to make sure.

But please, please, please for the 735th time, post this on a forum such as the Wrox one and make sure you provide more details. I can't help if you if I don't know what the problem is, what type of site you're using, the frame work you're targeting, the structure of you site and so on. This is *not* a support web site and as such not a great location to share details like this.

Cheers,

Imar
On Tuesday, February 15, 2011 9:00:35 PM alireza said:
My question is very clear,I want to change the database path,
I changed database path in Application_Start  and Session_Start function:

@"Provider=Microsoft.Jet.OLEDB.4.0;
                            Data Source=d:\Database\Website.mdb;
                            User ID=Admin;Password=";

but When I run web page i gettin message error :
'C:\Databases\Website.mdb' is not a valid path !!!!
while i changed database path and located  databse file(Website.mdb) in d:\Database
On Wednesday, February 16, 2011 7:34:55 AM Imar Spaanjaars said:
You and I must be having a different definition of clear. This is the first time you come close to telling what you have, what you need and what the problem is.

However, I don't understand why you think I can help you based on what you’ve told me. At the same time, I wonder what you'll do with my tips, as you've been ignoring all my advice and follow up questions since we started this discussion. Not once did you provide the information I requested and which I need to help you.

You also didn't follow up my advice by posting this on a proper forum, and supply relevant details.

Mind you: this article was written against ASP.NET 1.1. A lot has changed since. Since you didn't want to specify what version you run this against, and whether you're using a WAP (which compiles to an assembly at development time) or a WSP (which compiles on the fly) I can't even begin to recommend something without wasting an awful lot of time.

So, I do want to help you, but not here. I can help you, but not with the information you've provided so far. So: go over to http://p2p.wrox.com/index.php?referrerid=385, create a new post that answers each of the questions I've asked before and I might be able to help you.

Imar
On Saturday, April 16, 2011 2:33:43 AM RAVI LADHAWALA said:
Hi . I am beginner in development,I used this code to put my code, and change path databse .

(@"Provider=Microsoft.Jet.OLEDB.4.0;
  Data Source=..\App_Data\bashiri.mdb;
User ID=Admin;Password=";)

But when I run my web site i am getting message :

'C:\Databases\Website.mdb' is not a valid path.  Make sure that the path name is spelled correctly and that you are connected to the server on which the file resides.!!!!!!!

While I changed database path.

Please infor me where shuld i change ?
On Saturday, April 16, 2011 2:41:45 AM Imar Spaanjaars said:
Hi there,

And *where* did you change this path? If I recall correctly, the path is just stored in one place: Global.asax.

Cheers,

Imar
On Wednesday, May 25, 2011 3:53:40 AM Hemant said:
hello sir This is very helpful for me but my problem is : i am creating a matrimony site and when i visit others profile then my visit show on that person profile....... please help me if u have any solution
On Wednesday, September 28, 2011 1:17:21 PM syComputing said:
Imar thanks for this and many other useful articles you've authored that I've used as a reference in my coding.

My question is regarding strategies for accuracy using this method. For example, in testing my session_end code I set the sessionMode timeout value to 1 in web.config. I opened 2 browser sessions and noted that the total users and current users were accurate (both reported "2").

After the session timed out I then closed 1 browser and refreshed the other, upon which I noticed that the current user was correctly decremented to "1", however, the total users had increased to "3", which is not entirely accurate only in the sense that users may often leave an open browser session idle for a while for whatever reason.

Naturally, one answer would be to increase the timeout period by some value, but I'm wondering if there's an even more accurate way to handle the issue?

If not, what do you recommend as a good timeout value?

Many thanks!
On Wednesday, September 28, 2011 4:23:07 PM Imar Spaanjaars said:
Hi syComputing,

You could create a cookie that you send to the client. Then when you start a new session you can check for the existence of the cookie and when it's not there, you don't increase the "new user" counter.

Cheers,

Imar
On Wednesday, September 28, 2011 5:15:11 PM syComputing said:
Sounds perfect thanks for your reply.

I loved "Beginning ASP.NET 4 in C# and VB", btw...best beginners book I've come across yet.

Take care,
Sy
On Tuesday, December 13, 2011 11:20:59 PM Bryan Kelly said:
Hi Imar,

Thank you, your articles are very informative.

Would you be kind enough to give me some guidance?

I am setting up a site and was wondering ... could I use a similar principle to this, I would like to count visitor hits to my page - but equally count the users that complete the registration process and become members, therefore a membership counter.

I have searched the net for a while now and cannot seem to locate anything fit for purpose. There are many hit counters, but no registration/membership counters.

Is there any ASP code solution for capturing visitors that complete the registration process? I am aiming at developing a simple graphic (gif) that counts up with new members in real-time  ... similar to how paypal have a "transaction counter" on their homepage that reflects successful transactions.

Any help or signposting would be greatly appreciated.

Take care,

Bry
On Wednesday, December 14, 2011 4:43:35 PM Imar Spaanjaars said:
Hi Bryan,

Not sure if there's a ready-made solution, but if you control the registration process, this could be as simple as this hit counter. Rather than log a hit on Session_Start, just log it whenever your registration prcocess completes.

Cheers,

Imar
On Thursday, January 09, 2014 4:20:18 AM amol said:
this is nice but i want to store , number of hits everyday .

I want to find number of hits to page on particular date or day .

Is that possible ?
On Thursday, January 09, 2014 2:19:37 PM Imar Spaanjaars said:
Hi amol,

Yes, that's possible. Just include the current date in the UPDATE statement. You'll also need to insert a record with a unique date on the first hit of that day.

For the record: this article is almost 10 years old, and there are better alternatives available these days.

Cheers,

Imar
On Friday, January 10, 2014 4:03:19 AM amol said:
yes but i m not geting how to do that any can you gaive me any link for reference or any sample application ??  

i want such like this there are 365 days in year and i want 365 records in DB for each day say 50 hits amy 100 hits  .. if its today thats record need to update . and on tomorrow another record will insert .
On Friday, January 10, 2014 7:32:22 AM Imar Spaanjaars said:
Hi there,

I don't have a direct link. However, you'll need to have some basic understanding of SQL and ADO.NET data access code to accomplish this yourself, for which there are many tutorials:

https://www.google.com/#q=learn+sql&spell=1
http://www.w3schools.com/sql/default.asp?PHPSESSID=300ae3404d5fa2612f238abeebb8869c
https://www.google.com/#q=learn+ado.net
http://csharp-station.com/Tutorial/AdoDotNethttp://msdn.microsoft.com/en-us/library/h43ks021%28v=vs.71%29.aspx

Cheers,

Imar
On Friday, January 10, 2014 7:39:00 AM Amol said:
Soryy i know all this basic stuff .
On Friday, January 10, 2014 7:42:13 AM Imar Spaanjaars said:
Then you should be able to figure out how to do this, no?

In Session_Start do something like this:

- Query a record from the counter table matching today's date
- If it exists, update the counter and save the changes
- If it doesn't exist, create a new row and set the counter to 1. Then for the next visitor it will exists and will be updated.

Cheers,

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.