Sunday, September 2, 2012

URL Rewriting in ASP.NET



What is “URL rewriting”?
Most sites include variables in their URLs that tell the site what information to be shown to the user. It is something like telling the code to load particular details of item number 7 from a shopping site.
For example, the site may look like below:
The problem with the above URL is it cannot be remembered. It is even difficult to read on your mobile. Even the search engines like Google cannot get any information about the content of a page from the URL. What if you wanted to convey that itemid = 7 means a laptop with brand as “DELL”?  This is not speaking what it should speak.
So, what we expect from the above URL is it should be understandable just at the first look. Will it not be good if the URL is as below?
Now looking at the above URL you can easily tell that it is related to laptops and the brand is DELL. So, there is a need to rewrite URL to be somewhat meaningful and easily conveyable.
How to rewrite URL in ASP.NET?
To rewrite URLs in ASP.NET we are going to use HTTP Modules. HTTP Modules are called before and after the HTTP handler executes. HTTP modules help us to intercept, participate in, or modify each individual request. HTTP Modules implement IHttpModule interface, which is located in System.Web namespace. Modules are the ones which handles authentication and authorization of the ASP.NET applications. If you want to implement encryption of URL string or any custom changes to the application, it can be done by writing our HTTP Module.
Straight to the point, let us create a class in your app_Code directory called “URLRewriter.cs” and add the following code:
1.      Create a web site using visual studio and name it as “URLRewriteTestingApp” and add a class file “URLRewriter.cs”.
2.      Visual Studio will ask you that it will create a directory called “App_Code” and place the class file in the folder. Select “OK” to proceed.
Your solution structure should look something like this:



3.      Now open the file “URLRewriter.cs” and add the code for HTTP Module as below:

Basically your class must implement the IHttpModule interface to implement its methods. You will see these 2 methods created for you once you implement the interface.
-          Dispose
-          Init

Init method takes HttpApplication as a parameter. So, you need to create an event handler for begin request of the HttpApplication to handle the request.

Here is your code for the Module:
using System;
using System.Web;

/// <summary>
/// Summary description for URLRewriter
/// </summary>
public class URLRewriter : IHttpModule
{

    #region IHttpModule Members

    /// <summary>
    /// Dispose method for the class
    /// If you have any unmanaged resources to be disposed
    /// free them or release them in this method
    /// </summary>
    public void Dispose()
    {
        //not implementing this method
        //for this example
    }

    /// <summary>
    /// Initialization of the http application instance
    /// </summary>
    /// <param name="context"></param>
    public void Init(HttpApplication context)
    {
context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    /// <summary>
    /// Event handler of instance begin request
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void context_BeginRequest(object sender, EventArgs e)
    {
//Create an instance of the application that has raised the event
HttpApplication httpApplication = sender as HttpApplication;
       
//Safety check for the variable httpApplication if it is not null
         if (httpApplication != null)
         {
//get the request path - request path is    something you get in
               //the url
string requestPath = httpApplication.Context.Request.Path;

               //variable for translation path
            string translationPath = "";

//if the request path is /urlrewritetestingapp/laptops/dell/
            //it means the site is for DLL
            //else if "/urlrewritetestingapp/laptops/hp/"
            //it means the site is for HP
            //else it is the default path
            switch (requestPath.ToLower())
            {
                case "/urlrewritetestingapp/laptops/dell/":
translationPath = "/urlrewritetestingapp/showitem.aspx?itemid=7";
                    break;
                case "/urlrewritetestingapp/laptops/hp/":
translationPath = "/urlrewritetestingapp/showitem.aspx?itemid=8";
                    break;
                default:
translationPath = "/urlrewritetestingapp/default.aspx";
                    break;
            }

//use server transfer to transfer the request to the actual translated path
            httpApplication.Context.Server.Transfer(translationPath);
        }
    }

    #endregion
}

4.      Now open your default.aspx page in the solution and add the following html:
This code is to display two laptop brands HP and DELL.

Note: Since I am not deploying the application in IIS you will see the runtime host created by Visual Studio as localhost:8648. If your application has different port number or if you have hosted your web application in IIS, you may have to change the values of href in the following html.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Laptop Brands</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <a href="http://localhost:8648/URLRewriteTestingApp/laptops/Dell/">Dell
</a>
<br/>
<a href="http://localhost:8648/URLRewriteTestingApp/laptops/HP/">HP</a>
    </div>
    </form>
</body>
</html>
5.      Since we are placing references in the html code we do not need to do anything in the code behind file. Now add one more webform ShowItem.aspx in the website. This page is to display the actual data that clicking on the hyperlink in the default page should take it to. We are just trying to make the application look simple and so we just display the name of the laptop and a back button.
6.      HTML of the ShowItem.aspx page looks like as below:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ShowItem.aspx.cs" Inherits="ShowItem" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:Label runat="server" ID="labelShow"></asp:Label>
    <br/>
    <br/>
    <asp:Button runat="server" ID="back" Text="Back"/>
    </div>
    </form>
</body>
</html>

7.      Code behind file looks like as below:
Code behind is to display the text of the laptop and back button event handler that takes back the user back to the default page.

using System;

public partial class ShowItem : System.Web.UI.Page
{
    /// <summary>
    /// Page load event for the page
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Page_Load(object sender, EventArgs e)
    {
        //read the query string value of itemid that is coming
        //from default page when user clicks on the hyperlink
        string laptopName = Request.QueryString["itemid"];

        //if value is "8" display the text of the label as "HP"
        //else display the label as "DELL"
        labelShow.Text = laptopName == "8" ? "HP" : "DELL";

        //back button event handler
        back.Click += new EventHandler(back_Click);
    }

    /// <summary>
    /// Back button click event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void back_Click(object sender, EventArgs e)
    {
        //transfer the page back to default.aspx
        Server.Transfer("../default.aspx");
    }
}

8.      Now we need to configure our custom HttpModule “URLRewriter” in the configuration file to use the module.
<system.web>
<httpModules>
      <add name="URLRewriter" type="URLRewriter"/>
</httpModules>
</system.web>
Now our coding is complete and let us test our application by pressing F5 button.
Our default.aspx page:




When I place my cursor over Dell, the URL looks like http://localhost:8648/URLRewriteTestingApp/laptops/Dell/ and similarly if you place your cursor on HP, it should look like http://localhost:8648/URLRewriteTestingApp/laptops/HP/
Put break points in your HTTP Module methods and now click on any of the hyperlinks:
I clicked on the Dell and the value at the breakpoint now shows the value of requestPath.





Now the requestPath variable value is “/URLRewriteTestingApp/laptops/Dell/”.  Our rewriting code must now identify the requestPath and change the path to the actual URL path so that correct page gets called in the IIS by the engine. So, our translated path will be “/urlrewritetestingapp/showitem.aspx?itemdid=7”.



Now the page looks like below:




Clicking on the “Back” button will take the user to the default page.

Hope you like this article. If you have more number of URLs to be maintained in your application, it is better to handle the URLs by one dedicated server that processes the requests and redirects the request to the relevant page for processing.

Monday, August 20, 2012

Find difference of data between two data sources



Problem: User has got two sets of data sources. One is a table in Sql Server and other one is an excel sheet. So, we have 3 gridview controls in our web page. First grid shows the data from Database table and second one shows the data from Excel sheet and third one should show the difference of data between two grid views.

Solution:

Create a table in SQL server as below:

CREATE TABLE [dbo].[UserData](

      [ID] [int] IDENTITY(1,1) NOT NULL,

      [Name] [varchar](50) NULL,

      [Password] [varchar](50) NULL,

 CONSTRAINT [PK_UserData] PRIMARY KEY CLUSTERED

(

      [ID] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]



Enter data in the above table. I have entered the following data in the table:

ID
Name
Password
1
abc
123
2
xyz
321
3
NNN
789
4
PPP
8998



Here is the stored procedure to get the data from the table:

ALTER PROCEDURE [dbo].[GetUserData]

     

AS

     

      select Name,Password from userdata;

Now create an excel sheet document. It has two columns same as in the table above. One is “Name” and other one “password”.

Name
Password
abc
123
xyz
321
asas
856
jklo
854

Since, we need to compare two different sets of data, we need to make sure that the column names are same so that we can compare them using the features available in C# for .NET 3.5.

Now let us build the UI for displaying the data from two data sources and also the difference between the two data sets. My ASPX file looks like below:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="GridBind._Default" %>



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">



<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

    <title>Untitled Page</title>

</head>

<body>

    <form id="form1" runat="server">

    <div>

        <asp:Label runat="server" ID="lblDataDisplay">Display Data From Database</asp:Label>

        <asp:GridView runat="server" ID="DisplayDBData"> </asp:GridView>

   </div>

   <br/>

   <div>

       <asp:Label runat="server" ID="lblExcelDisplay">Display Data From Excel</asp:Label>

        <asp:GridView runat="server" ID="DisplayExcelData"></asp:GridView>

   </div>

   <br/>

   <div>

       <asp:Label runat="server" ID="Label1">Display Data Difference Between Database and Excel</asp:Label>

        <asp:GridView runat="server" ID="DisplayDifferenceData"></asp:GridView>

    </div>

  </form>

</body>

</html>



My Code behind file looks like below:

using System;

using System.Collections;

using System.Collections.Generic;

using System.Configuration;

using System.Data;

using System.Linq;

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;

using System.Data;

using System.Data.Sql;

using System.Data.SqlClient;

using System.Data.OleDb;



namespace GridBind

{

    public partial class _Default : System.Web.UI.Page

    {

        /// <summary>

        /// Page Load method

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        protected void Page_Load(object sender, EventArgs e)

        {

            //Get data from UserData Table from DB

            var ds = GetUserData();

            //Assign the data to the first gridview

            DisplayDBData.DataSource = ds;

            DisplayDBData.DataBind();



            DataSet dataSet;

            //Get data from Excel data source and bind it to second gridview

            var oleDbConnection = GetDataFromExcel(out dataSet);

            DisplayExcelData.DataSource = dataSet.Tables[0];

            DisplayExcelData.DataBind();

            oleDbConnection.Close();



            //Find the difference of data and bind the data to third gridview

            var dtAll = GetDataDifference(ds, dataSet);

            DisplayDifferenceData.DataSource = dtAll;

            DisplayDifferenceData.DataBind();





        }



        /// <summary>

        /// Gets data difference between two data sets

        /// </summary>

        /// <param name="ds"></param>

        /// <param name="dataSet"></param>

        /// <returns>DataTable value</returns>

  private static DataTable GetDataDifference(DataSet ds, DataSet      dataSet)

        {



            //Query first dataset

IEnumerable<DataRow> query1 = from userData in ds.Tables[0].AsEnumerable()

            select userData;



            //Query second dataset

IEnumerable<DataRow> query2 = from userData in dataSet.Tables[0].AsEnumerable()

            select userData;



            //Create data tables and get the data from above queries

            DataTable userData1 = query1.CopyToDataTable();

            DataTable userData2 = query2.CopyToDataTable();



//Now use Except operator to find the data in first set and not in second

var userDataFirstSet = userData1.AsEnumerable().Except(userData2.AsEnumerable(),

                                                           DataRowComparer.Default);

            //Find data in second and not in first

var userDataSecondSet = userData2.AsEnumerable().Except(userData1.AsEnumerable(),

                                                                 DataRowComparer.Default);

            //Create a new data table and add new columns

            DataTable dtAll = new DataTable();

            dtAll.Columns.Add(new DataColumn("Name", typeof (string)));

            dtAll.Columns.Add(new DataColumn("Password", typeof (string)));



//Bind the data from both user data sets and add it to the datatable

            foreach (var dataRow in userDataFirstSet)

            {

                DataRow dr = dtAll.NewRow();

                dr[0] = dataRow[0];

                dr[1] = dataRow[1];

                dtAll.Rows.Add(dr);

            }



            foreach (var dataRow in userDataSecondSet)

            {

                DataRow dr = dtAll.NewRow();

                dr[0] = dataRow[0];

                dr[1] = dataRow[1];

                dtAll.Rows.Add(dr);

            }

            return dtAll;

        }



        /// <summary>

        /// Method to get data from excel document

        /// </summary>

        /// <param name="dataSet"></param>

        /// <returns></returns>

        private static OleDbConnection GetDataFromExcel(out DataSet dataSet)

        {

            OleDbConnection oleDbConnection;

//Since I am using excel 2007, I have to specify the OLEDB connector for 2007

            OleDbDataAdapter oleDbDataAdapter;

            oleDbConnection =

                new OleDbConnection(

@"provider=Microsoft.ACE.OLEDB.12.0;Data Source='c: \UserData.xlsx';Extended Properties=Excel 12.0;");

oleDbDataAdapter = new OleDbDataAdapter("select rtrim(ltrim(name)) as [Name],rtrim(ltrim(password)) as [Password] from [Sheet1$]", oleDbConnection);

            oleDbDataAdapter.TableMappings.Add("Table", "ExcelTable");

            dataSet = new DataSet();

            oleDbDataAdapter.Fill(dataSet);

            return oleDbConnection;

        }



        /// <summary>

        /// Method to get data from database

        /// </summary>

        /// <returns></returns>

        private static DataSet GetUserData()

        {

            SqlConnection sqlConnection = new SqlConnection();

            sqlConnection.ConnectionString =

"Data Source=(local);Initial Catalog=MyTestDB;Persist Security Info=True;User ID=userid;Password=password;Pooling=False";

            SqlCommand sqlCommand = new SqlCommand();



            sqlCommand.Connection = sqlConnection;

            sqlCommand.CommandType = CommandType.StoredProcedure;

            sqlCommand.CommandText = "GetUserData";

            sqlConnection.Open();

            SqlDataAdapter adapter = new SqlDataAdapter(sqlCommand);

            DataSet ds = new DataSet();



            adapter.Fill(ds);

            return ds;

        }

    }

}



Now let us run our code and see the output:

Display Data From Database
NamePassword
abc123
xyz321
NNN789
PPP8998

Display Data From Excel
NamePassword
abc123
xyz321
asas856
jklo854

Display Data Difference Between Database and Excel
NamePassword
NNN789
PPP8998
asas856
jklo854


You can extend or change the program with more efficient code. This article was written by keeping in mind the first time programmers.