23 October 2015

Call Google Maps Geocode API in Parallel using C# and TPL

Google Maps Geocode API provides a way to validate addresses by getting latitude and longitude and address type of a given address. In this post I would like to show you how to call this web service in parallel so you can speed up the address validation process. One thing to note is URL to get Geocode details from Google Maps  API is different if you are only using free version in comparison to when you are using Google Maps API for work. By default Google Maps API provide 2500 request per day for free. When you are calling Google Maps API using paid version you need to encrypt your request using your client ID and encryption key provided to you when you buy it.

First thing first. You will need a key to run this application so sign up for Google Maps API and get your key https://developers.google.com/maps/documentation/geocoding/intro

You can download the full source code from GoogleGeocode.GoogleMapsAPI Source

Setup your app.config file
In AppSettings section add
 
  <add key="IsGoogleMapsAPIPaid" value="0"/>
   <add key="URL" value="https://maps.googleapis.com/maps/api/geocode/json?address="/>
   <add key="APIClient" value="yourclientId"/>
   <add key="APIKey" value="yourcryptokey"/>
In your code you will get a list of addresses you want to validate from your database. In this demo I have put some addresses in a list.
 
 List lstAddresses = new List() 
{ 
 "UNIT 7, 7 ERINDALE ROAD BALCATTA WA  6021",                            
 "226 MCINTYRE ROAD SUNSHINE VIC  3020",                           
 "UNIT 1 & 2, 12 PREMIER COURT WARANA QLD  4575",
 "PETERSHAM NSW  2049",
 "UNIT 7, 7 ERINDALE ROAD BALCATTA WA  6021",                            
 "226 MCINTYRE ROAD SUNSHINE VIC  3020",                           
 "UNIT 1 & 2, 12 PREMIER COURT WARANA QLD  4575",
 "WENTWORTHVILLE NSW  2049",
 "UNIT 7, 7 ERINDALE ROAD BALCATTA WA  6021",                            
 "MCINTYRE ROAD SUNSHINE VIC  3020",                           
 "UNIT 1 & 2, 12 PREMIER COURT WARANA QLD  4575",
 "PETERSHAM NSW  2049"
};

There is a limit of 10 API calls per second when you are calling Google Maps APIs so I am passing 10 addresses to Parallel.ForEach loop and calling Google Geocode API. This will parallalize API calls. Also there is a check if all API calls finish within one second wait for 1 second before calling next batch to avoid getting QUERY_OVER_LIMIT error.

 
Parallel.ForEach(selected, new ParallelOptions() { MaxDegreeOfParallelism = 10 }, sel =>
{
 try
 {
  string address = sel.ToString();
  Console.WriteLine("Address = " + address);
  GeoDetail objResult = GeoDetail.GetGeoDetails(key, address);
  lock (lockMe)
  {
   lstResult.Add(objResult);
  }

 }
 catch (Exception ex)
 {
  Console.WriteLine(ex.ToString());
 }

});

GetGeoDetails() method calls Google Maps API using HttpRequest and Get HttpResponse object back which then being converted to JSON object using JSON.Net library.
 
public static GeoDetail GetGeoDetails(string APIKey, string address)
{
 string uri = ConfigurationManager.AppSettings["URL"];
 GeoDetail objResult = new GeoDetail() { Address = address, Latitude = -1, Longitude = -1, AddressType = "", Error = "" };

 try
 {
  string requestURL = "";
  if (ConfigurationManager.AppSettings["IsGoogleMapsAPIPaid"].Trim() == "0")
  {
   requestURL = uri + address + "&key=" + APIKey; // No need to sign URL and there is no APIClient ID to pass.
  }
  else
  {
   requestURL = GoogleSignedUrl.Sign(uri + address + "&client=" + ConfigurationManager.AppSettings["APIClient"].Trim(), APIKey);
  }

  HttpWebRequest request = WebRequest.Create(requestURL) as HttpWebRequest;

  request.Accept = "application/json";

  // Get response  
  using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
  {
   if (response.StatusCode == HttpStatusCode.OK)
   {
    StreamReader readert = new StreamReader(response.GetResponseStream());
    string x = readert.ReadToEnd();
    JObject jObject = JObject.Parse(x);
    //Console.WriteLine(x);
    if (jObject["status"].ToString() == "OK")// successful API call
    {
     if (jObject["results"].Count() > 0)
     {
      string locationType = jObject["results"][0]["geometry"]["location_type"].ToString();

      string lat = jObject["results"][0]["geometry"]["location"]["lat"].ToString().Trim();
      string lng = jObject["results"][0]["geometry"]["location"]["lng"].ToString().Trim();

      Console.WriteLine("Geolocation lat lng :  {0}    {1}  Type  : {2}", lat, lng, locationType);

      double? latValue = null;
      double? lngValue = null;

      if (!string.IsNullOrWhiteSpace(lat))
      {
       latValue = double.Parse(lat);
      }
      if (!string.IsNullOrWhiteSpace(lng))
      {
       lngValue = double.Parse(lng);
      }

      objResult.Latitude = latValue;
      objResult.Longitude = lngValue;
      objResult.AddressType = locationType; //locationType == "ROOFTOP")//exact address match

     }
     else
     {
      Console.WriteLine("No result found");
      objResult.Error = "No result found";
     }
    }
    else
    {
     Console.WriteLine(jObject["status"].ToString());
     objResult.Error = jObject["status"].ToString();
    }


   }
  }

 }
 catch (Exception ex)
 {
  Console.WriteLine(ex.ToString());
  objResult.Error = ex.ToString();
 }

 return objResult;
}

08 October 2015

Search all tables in MSSQL for a specific value in all columns

I came across this little snippet which creates a stored procedure that can be used to query all the tables in MSSQL database to search for specific value. It may be useful to someone looking for a similar solution

 
CREATE PROC SearchAllTables
(
 @SearchStr nvarchar(100)
)
AS
BEGIN

    CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630))

    SET NOCOUNT ON

    DECLARE @TableName nvarchar(256), @ColumnName nvarchar(128), @SearchStr2 nvarchar(110)
    SET  @TableName = ''
    SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''')

    WHILE @TableName IS NOT NULL

    BEGIN
        SET @ColumnName = ''
        SET @TableName = 
        (
            SELECT MIN(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME))
            FROM     INFORMATION_SCHEMA.TABLES
            WHERE         TABLE_TYPE = 'BASE TABLE'
                AND    QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) > @TableName
                AND    OBJECTPROPERTY(
                        OBJECT_ID(
                            QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME)
                             ), 'IsMSShipped'
                               ) = 0
        )

        WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL)

        BEGIN
            SET @ColumnName =
            (
                SELECT MIN(QUOTENAME(COLUMN_NAME))
                FROM     INFORMATION_SCHEMA.COLUMNS
                WHERE         TABLE_SCHEMA    = PARSENAME(@TableName, 2)
                    AND    TABLE_NAME    = PARSENAME(@TableName, 1)
                    AND    DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar', 'int', 'decimal')
                    AND    QUOTENAME(COLUMN_NAME) > @ColumnName
            )

            IF @ColumnName IS NOT NULL

            BEGIN
                INSERT INTO #Results
                EXEC
                (
                    'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
                    FROM ' + @TableName + 'WITH (NOLOCK) ' +
                    ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2
                )
            END
        END    
    END

    SELECT ColumnName, ColumnValue FROM #Results
END

10 September 2015

Generic SQL Select Statement Executer in Java

In this post I will show you how to write a Java program that can allow you to run any SQL select statement against SQL Server and write output to csv file. While this is a simple task to use ResultSet and write output to file but imagine you need to keep on modifying output columns based on changes in business requirements or you need to output large number of SQL tables as csv files. In that case task gets little tedious and every time user request a new field to be added or removed you have to modify your class. This is where this trick comes handy.

You can download full source code from here : Generic SQL Select Statement Executer in Java

1. Lets create a new project in eclipse called SQLSelector and add a class file called GenericSelecor.java.

2. Next add sqljdbc4.jar and sqljdbc_auth.dll to your solution. This is required to connect to Microsoft SQL Server.

3. Add sqljdbc4.jar as a reference to your project by right clicking on project and going to properties and select java build path and libraries tab and click on Add Jar


4. Lets add two Properties file. One that is common for all SQL statements like database name,output folder etc and other one specific for current sql statement that will include Select statement, output file name and other specific information. I have Northwind database on my local machine and I want to select all customers from that database.

I have PROD.Properties file which contains database connection specific information.
Relative.Output.Folder=C\:\\SQLOutput\\
Server=localhost;instanceName=SQL2008
ODBC.DataSource=Northwind

I have Generic.Properties file which contains specific select statement and flag to indicate if string outputs should have double quotes
Statement=SELECT CustomerID ,CompanyName ,ContactName ,ContactTitle ,Address ,City ,Region ,PostalCode ,Country ,Phone ,Fax FROM Customers
OutputFileName = Customers.csv
StringOutputInDoubleQuote=Y

5. Now in GenericSelector class read those two properties files as arguments and read all the values in corresponding variables.

  
propertyFile = args[0];
statementFile= args[1];

Properties dpr = new Properties();
Properties spr = new Properties();

//Read all common variables from properties file
try 
{ 
 FileInputStream is = new FileInputStream(propertyFile);
 dpr.load(is);
 is.close();
   
 Server=dpr.getProperty("Server");
 DataSource=dpr.getProperty("ODBC.DataSource");
 
 OutputFolder=dpr.getProperty("Relative.Output.Folder");
 
}
catch(Exception e)
{
 e.printStackTrace();
 System.exit(1); 
}
// Read all specific variables from properties file
try 
{ 
 FileInputStream is = new FileInputStream(statementFile);
 spr.load(is);
 is.close();
 
 selectSatement =spr.getProperty("Statement"); // This is the select statement from external file
 outputFileName =spr.getProperty("OutputFileName");
 stringOutputInDoubleQuote = spr.getProperty("StringOutputInDoubleQuote").toUpperCase();
}
catch(Exception e)
{
 e.printStackTrace();
 System.exit(1);
 
}  


6.Load appropriate class driver and create connection to database. In this case I am using MSSQL server and connecting to Northwind database. Get a ResultSet by executing select statement. Get ResultSetMetaData from result set. This will help in identifying type of columns that are returned and name of those columns which then can be used to get the records from ResultSet without actually hard coding column names in application. ResultSetMetaData provides methods getColumnTypeName() and getColumnName() which are used to retrieve specific column information.
 
// Here we create a connection to database and run the query and then use metadata to generate csv output   
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
String sqlCmd = "";
con = DriverManager.getConnection("jdbc:sqlserver://"+Server+";databaseName="+DataSource+";integratedSecurity=true");

sqlCmd = selectSatement;
PreparedStatement stat = con.prepareStatement(sqlCmd);
ResultSet rs = stat.executeQuery(); // Actual Data
ResultSetMetaData rsmd = rs.getMetaData(); // Metadata from current result set.

int columnCount = rsmd.getColumnCount();

String[] columnTypes = new String[columnCount]; 
String[] columnNames = new String[columnCount];

StringBuilder sb = new  StringBuilder();
rows=0;
// The column count starts from 1
for (int i = 1; i < columnCount + 1; i++ ) 
{
 columnTypes[i-1] = rsmd.getColumnTypeName(i); // Get Column Type Names
 String name = rsmd.getColumnName(i);  // Get actual column names
 columnNames[i-1] = name;
 sb.append(name);
 sb.append( i==columnCount ? "\n" : ",");
}                       


7. Now Iterate through each records in ResultSet and for each record find value by using its column name and column type that was retrieved using ResultSetMetaData and write to output file. Here I have checked for type of the column so I can use specific get method. If you don't require any formatting then you can use getObject() method of result set without worrying about underlying record type.
 
String DATETIME_FORMAT_QRY = "dd/MM/yyyy";
java.text.SimpleDateFormat sdf2 = new java.text.SimpleDateFormat(DATETIME_FORMAT_QRY);
   
while(rs.next())
{
 
 rows++;
 // Based on column type call appropriate getXXX() method to get value from database
 // If you don't need specifc formatting based on data type you can use getObject() method instead of specific get method
 for (int i = 1; i < columnCount + 1; i++ ) 
 {
   // If output column type is string type then either output it in double quote or as is based on flag 
   if(columnTypes[i-1]== "nchar" || columnTypes[i-1]== "nvarchar")
    sb.append((rs.getString(columnNames[i-1]) == null ? "" : (stringOutputInDoubleQuote.equals("Y") ? "\""+ rs.getString(columnNames[i-1]).trim() + "\"" : rs.getString(columnNames[i-1]).trim())));
   else if(columnTypes[i-1]== "decimal")    
    sb.append(rs.getDouble(columnNames[i-1]));
   else if(columnTypes[i-1]== "int" || columnTypes[i-1]== "tinyint")   
    sb.append(rs.getInt(columnNames[i-1]));
   else if(columnTypes[i-1]== "datetime")   
    sb.append(rs.getDate(columnNames[i-1])==null ? "" : sdf2.format(rs.getDate(columnNames[i-1])));
   else  
    sb.append("ERROR IN INTERFACE : No check added for "+columnNames[i-1] );
  
  sb.append( i==columnCount ? "\n" : ",");
 }
 

 // Write to file
 if(rows>0)
 {
  if(rows==1) // Delete old file
  {
   File delfile = new File(OutputFolder+"/"+outputFileName);
   delfile.delete();
  }
  FileOutputStream outFile = new FileOutputStream(OutputFolder+"/"+outputFileName,true);
  outFile.write(sb.toString().getBytes());
  outFile.close();
  sb = new StringBuilder(); // Reset string builder for next iteration
  
 }
 
}                        


That is it!!! You have a fully functional java code that can create any table as csv output. All you need to do is change SQL in Generic.Properties file. Happy Coding !!!

07 September 2015

Only allow digits in Console Application in C#

If you are writing a console application in C# and you want to restrict user to only enter digits for certain variable there is an option to use ConsoleKeyInfo struct to read each key user input and take action accordingly. This struct provides a way to find which key user has entered in console application and check if it is a number or not using Char.IsNumber() method.
Below is the complete source code that only allow user to enter digits for a field. If user type any other characters it simply ignores them.

 
Console.WriteLine("Enter Numeric Value : ");
ConsoleKeyInfo key;
string inputStr = "";
do
{
 key = Console.ReadKey(true);
 if (key.Key != ConsoleKey.Backspace && key.Key != ConsoleKey.Enter)
 {
  if (char.IsNumber(key.KeyChar))//Check if it is a number
  {
   inputStr += key.KeyChar;
   Console.Write(key.KeyChar);
  }
 }
 else
 {
  if (key.Key == ConsoleKey.Backspace && inputStr.Length > 0)
  {
   inputStr = inputStr.Substring(0, (inputStr.Length - 1));
   Console.Write("\b \b");
  }
 }

} while (key.Key != ConsoleKey.Enter);

Console.WriteLine("\nNumber you entered is {0}", inputStr);

04 September 2015

Find Distinct Objects from List of Objects using LINQ

To find distinct values from list of values in C# is a one line task by using LINQ's Distinct() method. This works well with primitive types but if you run the same method on List of custom Objects you will not get distinct objects based on its properties. To achieve this you an option to implement IEqualityComparer interface and use it to find distinct objects based on its properties. In the implementation of  Equals method you can define which properties to check for equality.

Below code provides the complete solution to get distinct objects from list of objects.


 
 using System.Collections.Generic;
    public class Team
    {
        public string Name {get;set;}
        public int Score {get;set;}
    }
        //Create some dummy data with duplicates
        public List<Team> lstTeam = new List<Team>{
        new Team{Name="Brazil", Score=1},
        new Team{Name="Man U", Score=1},
        new Team{Name="Man U", Score=1},
        new Team{Name="Brazil", Score=2},
        new Team{Name="Man U", Score=2},
        new Team{Name="Brazil", Score=2}

        };   

    //This is where we use equality comparer implementation to find unique records
     List<Team> lstDistictTeams = lstTeam.Distinct<Team>(new DistinctComparer()).ToList();

     foreach(Team t in lstDistictTeams) // Output Distinct Objects
     {
         Console.WriteLine("Team {0} has Score {1}",t.Name,t.Score);
     }

    //This class provides a way to compare two objects are equal or not
     public class DistinctComparer : IEqualityComparer<Team>
        {
            public bool Equals(Team x, Team y)
            {
                return (x.Name == y.Name && x.Score == y.Score); // Here you compare properties for equality
            }
            public int GetHashCode(Team obj)
            {
                return (obj.Name.GetHashCode() + obj.score.GetHashCode());
            }
        } 

14 May 2015

Copy Data between two different MSSQL Databases on different servers using C#

You can copy data from one SQL table to another using INSERT command with SELECT within same database or databases on same server but things gets little complicated when databases are on two different server. Here is a C# snipplet you can use to copy data between two desperate databases on two different servers. Code is self explanatory with comments.  Your source and destination table fields needs to match.


// Create source connection
SqlConnection source = new SqlConnection(ConfigurationManager.ConnectionStrings["SourceConnectionString"].ConnectionString);
// Create destination connection
SqlConnection destination = new SqlConnection(ConfigurationManager.ConnectionStrings["DestinationConnectionString"].ConnectionString);


// Open source and destination connections.
source.Open();
destination.Open();

// Select data from Products table
SqlCommand cmd = new SqlCommand(@"SELECT customer
         ,name
         ,address1
         ,address2
         ,address3
         ,address4
         ,address6
         ,address5
         ,fax
         ,territory
         ,region
         ,class
        FROM
        [dbo].[slcustm]", source);
// Execute reader
Console.WriteLine("Read data  from [dbo].[slcustm] table");
SqlDataReader reader = cmd.ExecuteReader();

Console.WriteLine("Write data  to DestinationCustomers table");
// Create SqlBulkCopy

SqlBulkCopy bulkData = new SqlBulkCopy(destination);
//If you are copying larger amount of data don't forget to set the timeout flag. Default value is 30 seconds. 0 = No limit
bulkData.BulkCopyTimeout = 0;

// Set destination table name
bulkData.DestinationTableName = "DestinationCustomers";
// Write data
bulkData.WriteToServer(reader);
// Close objects
bulkData.Close();
destination.Close();

03 May 2015

ASP.NET Gridview with Filter in Header using Reflection and LINQ

Introduction

ASP.NET gridview by default provides facility for sorting and paging but no inbuilt facility to filter column. This article looks at possible way to implement filtering function within the Gridview.

Background

I came across this requirement of having a gridview which allows filtering data from within the gridview. I also wanted to preserve the sorting and paging of the gridview.
Rather than creating separate panel above gridview for each of the fields to filter data, wouldn't it be nice to put a textbox along with each header column to filter data.
This leads me to this solution I derived for it. This may not be the best solution to do it but it definitely works. Our goal is to achieve this.

You can download full source code from : ASP.NET Gridview with Filter in Header Source Code


How It All Works ?

Create ASP.NET Web Application project in Visual Studio. First of all, we will create a DTO class to hold some data that we can display in a gridview. For this demo, I have created a DTO class of outstanding orders that contains some properties and some dummy data.

[Serializable]
public class Outstanding
{
 public string Item { get; set; }
 public string Order { get; set; }
 public int Line { get; set; }
 public int Status { get; set; }
 public string ToLocation { get; set; }
 public decimal Qty { get; set; }
 public DateTime RegDate { get; set; }
 public string Location { get; set; }
 public decimal AllocQty { get; set; }

 public List GetOutstanding()
 {
  List lstOrders = new List();

  lstOrders.Add(new Outstanding() { Item = "CocaCola", 
  Order = "000101", Line = 1, Status = 20, 
  ToLocation = "Sydney", 
  Qty = 2000, RegDate = new DateTime(2014, 1, 1), 
  Location = "USA", AllocQty = 100 });
  lstOrders.Add(new Outstanding() { Item = "BubbleGum", 
  Order = "000101", Line = 1, Status = 20, 
  ToLocation = "Sydney", 
  Qty = 2500, RegDate = new DateTime(2014, 1, 11), 
  Location = "USA", AllocQty = 300 });
  lstOrders.Add(new Outstanding() { Item = "Coffee", 
  Order = "000111", Line = 1, Status = 50, 
  ToLocation = "Melbourne", 
  Qty = 2500, RegDate = new DateTime(2014, 1, 10), 
  Location = "USA", AllocQty = 100 });
  lstOrders.Add(new Outstanding() { Item = "Sugar", 
  Order = "000112", Line = 1, Status = 50, 
  ToLocation = "Melbourne", 
  Qty = 2300, RegDate = new DateTime(2014, 1, 10), 
  Location = "NZ", AllocQty = 300 });
  lstOrders.Add(new Outstanding() { Item = "Milk", 
  Order = "000112", Line = 1, Status = 50, 
  ToLocation = "Melbourne", 
  Qty = 2300, RegDate = new DateTime(2014, 1, 10), 
  Location = "NZ", AllocQty = 200 });
  lstOrders.Add(new Outstanding() { Item = "Green Tea", 
  Order = "000112", Line = 1, Status = 20, 
  ToLocation = "Melbourne", 
  Qty = 300, RegDate = new DateTime(2014, 1, 10), 
  Location = "NZ", AllocQty = 220 });
  lstOrders.Add(new Outstanding() { Item = "Biscuit", 
  Order = "000131", Line = 1, Status = 70, 
  ToLocation = "Perth", 
  Qty = 200, RegDate = new DateTime(2014, 1, 12), 
  Location = "IND", AllocQty = 10 });
  lstOrders.Add(new Outstanding() { Item = "Wrap", 
  Order = "000131", Line = 1, Status = 20, 
  ToLocation = "Perth", 
  Qty = 2100, RegDate = new DateTime(2014, 1, 12), 
  Location = "IND", AllocQty = 110 });

  return lstOrders;
 }
}
Now in the Default.aspx page, add a gridview. To preserve sorting, add link button in HeaderTemplate with CommandName as "Sort" and CommandArgument as name of the column. Also, for the purpose of filtering the application will bind all the textboxes to single event (OnTextChanged="txtItem_TextChanged" ) and within the event we will determine which textbox fired it and take action accordingly.  So columns of the gridview will look like this. I have used different filters like =,>,<,>=&<= for numeric data and "contains" filter for string values.
Note : Make sure you name all your textboxes as txtFieldName so when filtering we can remove the txt from the ID of the textbox and then use reflection and LINQ to filter the data.
<asp:TemplateField SortExpression="Item">
    <HeaderTemplate>
      <asp:LinkButton ID="lbItem" runat="server" Text="Item" 
      CommandName="Sort" CommandArgument="Item"></asp:LinkButton>
      <br />
      <asp:TextBox runat="server" ID="txtItem" AutoPostBack="true" 
      OnTextChanged="txtItem_TextChanged"></asp:TextBox>
    </HeaderTemplate>
    <ItemTemplate>
       <%#Eval("Item") %>
    </ItemTemplate>
    </asp:TemplateField><asp:TemplateField SortExpression="Line" 
    ItemStyle-HorizontalAlign="Right" HeaderStyle-HorizontalAlign="Right">
     <HeaderTemplate>
        <asp:LinkButton ID="lbLine" runat="server" Text="Line" 
        CommandName="Sort" CommandArgument="Line"
        CssClass="RightAlign"></asp:LinkButton>
      <br />
     <table>
      <tr>
       <td>
        <asp:DropDownList runat="server" 
        ID="ddlFilterTypeLine" CssClass="upperCaseText">
        <asp:ListItem Text="=" Value="=" 
        Selected="True"></asp:ListItem>
        <asp:ListItem Text=">" Value=">"></asp:ListItem>
         <asp:ListItem Text=">=" Value=">="></asp:ListItem>
         <asp:ListItem Text="<" Value="<"></asp:ListItem>
        <asp:ListItem Text="<="  Value="<="></asp:ListItem>
         </asp:DropDownList>
      </td>
     <td>
     <asp:TextBox runat="server" ID="txtLine" Width="50" 
     AutoPostBack="true" OnTextChanged="txtItem_TextChanged" 
     CssClass="upperCaseText"></asp:TextBox>
    </td>
</tr>
</table></HeaderTemplate>
<ItemTemplate>
<%#Eval("Line","{0:0}")%>
</ItemTemplate>
</asp:TemplateField> 
Now in the Page_Load event, we will bind gridview to the dummy data. I have kept data in ViewState for this demo.
if (!Page.IsPostBack)
{
 Outstanding objOutstanding = new Outstanding();

 List lstOutstandingOrders = new List();
 lstOutstandingOrders = objOutstanding.GetOutstanding();

 ViewState["columnNameO"] = "RegDate";
 grdViewOutstanding.DataSource = lstOutstandingOrders;
 grdViewOutstanding.DataBind();

 ViewState["lstOutstandingOrders"] = lstOutstandingOrders;
 upnlOutstanding.Update();
}  
In the textbox's text change event, we will find out which textbox fired it by looking at the ID of a sender and take action accordingly. Finally, we will bind the data to gridview. To preserve the values in filter after postback, I created a seperate method which gets called everytime postback occurs and set values in corresponding textboxes and filters after postback.

In here what happens is all the textboxes are bound to single event so when even is fired you will first find out which textbox has fired that event and remove txt from the ID of textbox to get the name of the property to filter. x.GetType().GetProperty(filterName).GetValue(x, new object[] { } ) provides the value of associated property from list of objects. This is using reflection to get the property of Outstanding class based on input "filterName" as string value and then get the value of the property from the object and compare it to what is passed in the textbox.

 
        // For Outstanding Orders - Single Event bound  to all textboxes
        protected void txtItem_TextChanged(object sender, EventArgs e)
        {
            if (ViewState["lstOutstandingOrders"] != null)
            {
                List allOutstanding = (List)ViewState["lstOutstandingOrders"];
                TextBox txtBox = (TextBox)sender;
                string filterName = txtBox.ID.Substring(3); // remove txt from Textbox ID. You need to make sure that all the textboxes for filtering are named as txtFieldName
                //Check if there is a dropdown associated with current filter.
                if (grdViewOutstanding.HeaderRow.FindControl("ddlFilterType" + filterName) != null)
                {
                    //Get value from filter type dropdown
                    string filtrerType = ((DropDownList)grdViewOutstanding.HeaderRow.FindControl("ddlFilterType" + filterName)).SelectedItem.Value;

                    //Special case for DateTime
                    if (filterName == "RegDate")
                    {
                        DateTime filterValue = DateTime.Parse(txtBox.Text.Trim());
                        //Use LINQ reflection to find value for the input filer value
                        //x.GetType().GetProperty(filterName).GetValue(x, new object[] { })-- This is the LINQ reflection to get value 
                        //x.GetType().GetProperty(filterName) -- This gives you the actual property associated with Outstanding class based on input property name as string value. 
                        //Then we call get value to get its acutal value and compare it with what is being passed into textbox
                        if (filtrerType == "=")
                            allOutstanding = allOutstanding.Where(x => DateTime.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) == filterValue).ToList();
                        else if (filtrerType == ">")
                            allOutstanding = allOutstanding.Where(x => DateTime.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) > filterValue).ToList();
                        else if (filtrerType == ">=")
                            allOutstanding = allOutstanding.Where(x => DateTime.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) >= filterValue).ToList();
                        else if (filtrerType == "<")
                            allOutstanding = allOutstanding.Where(x => DateTime.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) < filterValue).ToList();
                        else if (filtrerType == "<=")
                            allOutstanding = allOutstanding.Where(x => DateTime.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) <= filterValue).ToList();
                    }
                    else // Parse Numbers as decimal
                    {
                        if (filtrerType == "=")
                            allOutstanding = allOutstanding.Where(x => decimal.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) == decimal.Parse(txtBox.Text.Trim())).ToList();
                        else if (filtrerType == ">")
                            allOutstanding = allOutstanding.Where(x => decimal.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) > decimal.Parse(txtBox.Text.Trim())).ToList();
                        else if (filtrerType == ">=")
                            allOutstanding = allOutstanding.Where(x => decimal.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) >= decimal.Parse(txtBox.Text.Trim())).ToList();
                        else if (filtrerType == "<")
                            allOutstanding = allOutstanding.Where(x => decimal.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) < decimal.Parse(txtBox.Text.Trim())).ToList();
                        else if (filtrerType == "<=")
                            allOutstanding = allOutstanding.Where(x => decimal.Parse(x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString()) <= decimal.Parse(txtBox.Text.Trim())).ToList();
                    }
                    //Hold Filter Type in ViewState to preserve what is selected for use during postback
                    ViewState["OFilter" + filterName] = filtrerType;
                }
                else // Only string value
                {
                    allOutstanding = allOutstanding.Where(x => x.GetType().GetProperty(filterName).GetValue(x, new object[] { }).ToString().ToUpper().Contains(txtBox.Text.Trim().ToUpper())).ToList();
                }

                //Hold the filter value in ViewState. This will be used in ResetFilterAndValueOutstanidn() during postback
                ViewState["O" + filterName] = txtBox.Text.Trim().ToUpper();

                ViewState["lstOutstandingOrders"] = allOutstanding;
                grdViewOutstanding.DataSource = allOutstanding;
                grdViewOutstanding.DataBind();

                ResetFilterAndValueOutstanding();

            }


        } 
ResetFilterAndValueOutstanding() method restores values in filter textbox and filter type in dropdown after each postback. All the filter values and filter types are stored in ViewState with key value starting with "O". Make sure you don't store any other data in ViewState with key value starting with "O" because when removing the filter we will remove all the ViewState values starting with "O".
        protected void ResetFilterAndValueOutstanding()
        {
            //All the filters and filtervalues are stored in ViewState staring with "O"
            foreach (var k in ViewState.Keys)
            {
                if (k.ToString().StartsWith("O"))
                {
                    //Check if there is a textbox in GridView Header for this ViewState value. 
                    if (grdViewOutstanding.HeaderRow.FindControl("txt" + k.ToString().Substring(1)) != null)
                    {
                        ((TextBox)grdViewOutstanding.HeaderRow.FindControl("txt" + k.ToString().Substring(1))).Text = ViewState[k.ToString()].ToString().ToUpper();
                    }
                    //Check if there is a dropdownlist in GridView for this ViewState value.
                    if (grdViewOutstanding.HeaderRow.FindControl("ddlFilterType" + k.ToString().Substring(1)) != null)
                    {
                        foreach (ListItem li in ((DropDownList)grdViewOutstanding.HeaderRow.FindControl("ddlFilterType" + k.ToString().Substring(1))).Items)
                        {
                            if (li.Text == ViewState["OFilter" + k.ToString().Substring(1)].ToString()) li.Selected = true; else li.Selected = false;
                        }
                    }
                }
            }
          
        }
Add a link button on top of the gridview called "Remove Filter" which will remove all the ViewState with keys starting with "O" and rebind gridview to data and reset all filters to its original values.
protected void lbRemoveFilterOutstanding_Click(object sender, EventArgs e)
        {
            //Find all the ViewState Keys starting with "O". This represents Filters and Filter Values
            List lstKeysToRemove = new List();
            foreach (var k in ViewState.Keys)
            {
                if (k.ToString().StartsWith("O"))
                {
                    lstKeysToRemove.Add(k.ToString());
                }
            }
            foreach (string key in lstKeysToRemove)
            {
                ViewState.Remove(key);
            }
         
            Outstanding objOutstanding = new Outstanding();
            List lstOutstandingOrders = new List();
            lstOutstandingOrders = objOutstanding.GetOutstanding();
          
            grdViewOutstanding.DataSource = lstOutstandingOrders;
            grdViewOutstanding.DataBind();

            ViewState["lstOutstandingOrders"] = lstOutstandingOrders;
        }
There is paging and sorting enabled on the gridview which is easy to understand. This is one of the way to implement filtering on a gridview with paging and sorting.

Happy Coding !!!