Recommend Or Share with your Friends

A Beginner's Tutorial for Understanding Filters and Attributes in ASP.NET MVC

Introduction

A Beginner's Tutorial for Understanding Filters and Attributes in ASP.NET MVC

 

In this article we will try to see how we can use custom filters and attributes in an ASP.NET MVC application. Custom filters and attributes are an excellent way of injecting extra processing logic into the MVC request response pipeline. We will try to understand all about these and will see them in action using a simple sample application.

Background

In an ASP.NET MVC application the request from the user first lands at the UrlRoutingModule. This module parses the requested URL and then invokes the corresponding controller and action. The controller will then render the appropriate view and the response will be sent to the user.
Now what if we want to inject some extra processing logic in this request-response life cycle. Some extra logic that is written once and can be reused across multiple controllers and/or actions.
ASP.NET MVC provides a way for us to do that by writing custom filters that can be used to inject extra processing logic in the request-response life cycle.

What are attributes and filters

MVC provides a very clean way of injecting the pre-processing and post-processing logic for actions and controllers. They way we can put the pre-processing and post-processing logic is by decorating the actions with attributes which will invoke an attribute class implementing the filter's logic.
For example, If we need some action to be executed when the user has been authenticated then we can adorn the action with the [Authorize] attribute. This will take care of calling the attribute class which implements the authorization filter to check whether the user has is authorized or not.
[Authorize]
public ActionResult Index()
{
    return View();
}
So the way to implement custom filters would be to implement the interface that is needed for implementing the required filter. Now we can decorate the actions with this attribute so that our filter logic will be executed when this action is called. If we want all the actions of a controller to use this filter we can decorate the controller itself with this attribute.

Using the code

Let us now try to look at the type of filters we can implement to inject our custom processing logic.

Type of filters

Now taking this discussion further, Let us first discuss the various types of filters that can be implemented to inject custom processing logic.
  • Authorization filter
  • Action filter
  • Result filter
  • Exception filter

Implementing Custom Filters

Now let us try to look at implement these filters. We will simply implement the custom filters and put a simple message in the ViewBag collection. We will then use these filters with an action of controller and try to see the custom messages we inserted in the ViewBag collection on our view page.

Authorization filter

This filter provides authentication and authorization logic. It will be executed before the action gets executed. To implement this action the interface IAuthorizationFilter should be implemented by the custom attribute class.
public class CustomAuthorizationAttribute : FilterAttribute, IAuthorizationFilter
{
    void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext)
    {
        filterContext.Controller.ViewBag.OnAuthorization = "IAuthorizationFilter.OnAuthorization filter called";
    }
}
Now when we decorate the action method with this attribute the OnAuthorize filter method will be called and our custom logic will get executed.
Note: In the above code we have created an attribute which will only run when the authorization is being done by the application. In our own filter method we are not doing anything related to authorization. If we were to do custom authentication and authorization then we will have to derive this attribute from AuthorizeAttribute class and implement custom authorization logic. Perhaps we will discuss that separately. For now this filter will run run when the authorization is being done and before calling the action method so that we can inject our custom logic in it.

Action filter

This filter will be called before and after the action starts executing and after the action has executed. We can put our custom pre-processing and post-processing logic in this filter.
Now to implement this filter we need to create a custom filter attribute class and implement the IActionFilter filter interface. This interface provides us two methods OnActionExecuting and OnActionExecuted which will be called before and after the action gets executed respectively.
public class CustomActionAttribute : FilterAttribute, IActionFilter
{
    void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
    {
        filterContext.Controller.ViewBag.OnActionExecuted = "IActionFilter.OnActionExecuted filter called";
    }

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.Controller.ViewBag.OnActionExecuting = "IActionFilter.OnActionExecuting filter called";
    }
}

Result filter

This filter will execute before and after the result of the action method has been executed. We can use this filter if we want some modification to be done in the action's result.
To implement the result filters we need to create a custom filter attribute class and implement the IResultFilter interface. this interface provides two methods OnResultExecuting and OnResultExecuted which will be called before and after the action result respectively.
public class CustomResultAttribute : FilterAttribute, IResultFilter
{
    void IResultFilter.OnResultExecuted(ResultExecutedContext filterContext)
    {
        filterContext.Controller.ViewBag.OnResultExecuted = "IResultFilter.OnResultExecuted filter called";
    }

    void IResultFilter.OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.Controller.ViewBag.OnResultExecuting = "IResultFilter.OnResultExecuting filter called";
    }
}

Exception filter

This filter will be invoked whenever a controller or action of the controller throws an exception. This is particularly useful when we need custom error logging module.
To implement this filter we need to create a custom filter attribute class which implements IExceptionFilter. This interface gives us a methods called OnException which is a perfect place to call the exception logging module and to redirect to some error page.
public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
{       
    void IExceptionFilter.OnException(ExceptionContext filterContext)
    {
        filterContext.Controller.ViewBag.OnException = "IExceptionFilter.OnException filter called";
    }
}

Order of Execution

Now with all the above filters we have the following filter methods.
  • IAuthorizationFilter.OnAuthorization
  • IActionFilter.OnActionExecuted
  • IActionFilter.OnActionExecuting
  • IResultFilter.OnResultExecuted
  • IResultFilter.OnResultExecuting
  • IExceptionFilter.OnException
Now assuming that we have all the filters attached to a single action method what will be the order of execution of these filers. These filters will execute in following order under normal(non-exception) scenario.
  1. IAuthorizationFilter.OnAuthorization
  2. IActionFilter.OnActionExecuting
  3. IActionFilter.OnActionExecuted
  4. IResultFilter.OnResultExecuting
  5. IResultFilter.OnResultExecuted
In case there is an exception, OnException will will be called as instead of the result filters.

Using the Custom Filters

Now from our application we just need to decorate the actions on which we need the custom filter functionality. Lets try to do this on a single action method as:
public class HomeController : Controller
{   
    [CustomAuthorization]
    [CustomAction]
    [CustomResultAttribute]
    [CustomExceptionAttribute]
    public ActionResult Index()
    {
        //throw new Exception("Dummy Exception");
        ViewBag.Message = "Index Action of Home controller is being called.";
        return View();
    }
} 
 
The important thing to note in the running application is that ViewBag.OnResultExecuted is empty. the reason for this is that the function IResultFilter.OnResultExecuted gets called when the view has been rendered i.e. the action result has been completed.
Note: It is advisable to put breakpoints on all filter methods and then run the application to understand the sequence of these filter methods. Also, un-commenting the line in controller which throws a dummy exception will invoke the IExceptionFilter.OnException filter method too.

Built-in Attributes 

ASP.NET MVC comes with a some of built in attribute classes that provides a some boilerplate functionality. We can create custom classes that derives from these built in classes and further provide specialized behavior as per our needs. Let us try to see some of these built in attributes.
  • AuthorizeAttribute: MVC framework provides AuthorizeAttribute which is helpful in specifying our custom authorization policies.
  • ActionFilterAttribute: This is the built in implementation of IActionFilter and IResultFilter. This attribute can be used as base class to implement the custom behavior for action and result filters.
  • HandleErrorAttribute: This is the built in implementation of IExceptionFilter which makes it easier to implement the exception handling strategy.

Point of interest 

This was an introductory article for beginner's to make then familiar with the concept of filters and attributes in ASP.NET MVC application. We discussed how custom filters and attributes are helpful in injecting custom pre-processing and/or post-processing logic in the MVC request-response cycle. I hope this has been little informative.

History

  • 15 April 2013: First version 

source code.project.com
 
By www.DrupalStudy.com
Saturday, July 5, 2014
Tag :

AJAX-enabled Name Selector User Control

AJAX-enabled Name Selector User Control

 

 Introduction

The more I use sites like Facebook, the more fascinated I become with the controls they use. One control that I've been obsessed about is the one used to find other users. As you type, a very well formatted drop-down shows names with other information. Selecting one of those names then triggers more server-side work. This article is my attempt to develop a similar experience with the Microsoft stack: SQL for the database, ASP.NET 2.0 for the code, the AJAX Control Kit for the sizzle, and a splash of JavaScript because there's just no way around it.

Background

I have made an attempt at this type of control before, but was largely unhappy with the result. The biggest problem with my past attempt was that it used the CallBack technology which leans heavily on JavaScript and sending text across the wire. There are many issues with this approach... and while admittedly leaner, offers less control than I prefer. Instead, I wanted an approach were the user experience felt client-side, but in fact I was able to manipulate things on the server and deliver meaningful and well-formatted results. The only way I could figure to accomplish this is to use Asynchronous Postbacks. One major draw-back to using postbacks, however, is that controls tend to lose their focus after a postback (asynch or not!), which is a disaster in a text box where the user expects to be able to type freely without having to re-select the box, so I needed to overcome that as well.
One other goal of this control is something completely portable in my application. This control is designed to be dropped on any page of my application in any location without any code configuration required.

Key Point #1 - The Database Stuff

Certainly, a search like this could work in a lot of scenarios. The example attached to this article focuses on people. In SQL Server 2005, I have three tables.
  • A Person table with Firstname, Lastname, JobTitleID, and LocationID.
  • A JobTitle table with JobTitleID, and JobTitle.
  • A Location table with LocationID and Location.
I then use the following Stored Procedure to search for matching people. The important part of this Stored Procedure is the WHERE clause. I am matching the string entered by the user by First Name, OR Last Name, OR First <space> Last, OR Last <comma><space> First.
CREATE PROCEDURE usp_searchPeople (@namecontains nvarchar(20))
AS

    SELECT
        P.FirstName,
        P.LastName,
        J.JobTitle,
        L.Location
    FROM
        Person as P 
        INNER JOIN JobTitle as J on P.JobTitleID = J.JobTitleID
        INNER JOIN Location as L on P.LocationID = L.LocationID
    WHERE
        @namecontains <> ''
        AND
        P.LastName like N'%' + @namecontains + '%'
        OR P.Firstname like N'%' + @namecontains + '%'
        or ((P.LastName + ', ' + P.FirstName) like N'%' + @namecontains + '%')
        OR ((P.FirstName + ' ' + P.LastName) like N'%' + @namecontains + '%')
    ORDER BY 
        P.Lastname, P.FirstName

Key Point #2 - The Classes

I really like using ObjectDataSources on my designer pages. Using them sort of forces you to abstract the database interaction into classes - which is a good thing in the world of OO. So, here are my two People classes: one is just the Person class, while the People class handles getting the data and returning a DataSet (a requirement if we want an ObjectDataSource to retrieve and ultimately populate a GridView). Nothing ground-breaking about these classes. However, in the People class, I am using the Application.Data DLL to fetch the data from SQL Server. If you are not familiar...you really should be. It is really nice. Also, I have set the database connection in my Web.Config (something you will have to adjust if you are going to test this).
Public Class Person

    Private _Firstname As String

    Public Property Firstname() As String
        Get
            Return _Firstname
        End Get
        Set(ByVal value As String)
            _Firstname = value
        End Set
    End Property


    Private _Lastname As String

    Public Property Lastname() As String
        Get
            Return _Lastname
        End Get
        Set(ByVal value As String)
            _Lastname = value
        End Set
    End Property


    Private _JobTitle As String

    Public Property JobTitle() As String
        Get
            Return _JobTitle
        End Get
        Set(ByVal value As String)
            _JobTitle = value
        End Set
    End Property


    Private _Location As String

    Public Property Location() As String
        Get
            Return _Location
        End Get
        Set(ByVal value As String)
            _Location = value
        End Set
    End Property


End Class

Imports Microsoft.ApplicationBlocks.Data
Public Class People

    Public Function SearchPeople(ByVal searchstr As String) As DataSet
        Dim strSQL As String = "usp_SearchPeople"
        Dim params(0) As SqlClient.SqlParameter
        params(0) = New SqlClient.SqlParameter("@namecontains", searchstr)


        Try
            PeopleDS = SqlHelper.ExecuteDataset(_
              ConfigurationManager.AppSettings("MyDataBase"), _
              CommandType.StoredProcedure, strSQL, params)
        Catch ex As Exception
            _ErrMsg = ex.Message
        End Try

        Return PeopleDS

    End Function

    Private _ErrMsg As String

    Public Property ErrMsg() As String
        Get
            Return _ErrMsg
        End Get
        Set(ByVal value As String)
            _ErrMsg = value
        End Set
    End Property


    Private _PeopleDS As DataSet = New DataSet()
    Public Property PeopleDS() As DataSet
        Get
            Return _PeopleDS
        End Get
        Set(ByVal value As DataSet)
            _PeopleDS = value
        End Set
    End Property


    Private _Person As Person

    Public Property Person() As Person
        Get
            Return _Person
        End Get
        Set(ByVal value As Person)
            _Person = value
        End Set
    End Property

End Class

Key Point #3 - The User Control Designer Page

We now have enough to create our User Control designer page. The page will have a lot of stuff on it before we're done, but here are the important parts:
  • Search Box - a box where the user will type with a couple JavaScript references. I'll talk about those functions in section 4.
  • <asp:TextBox runat="server" ID="txtSearchStr" 
        Width="260" style="border:none;"
        onfocus="startSearching(this.id)" onblur="endSearching()" />
  • Update Panel - I wrap the rest of the control in an UpdatePanel so the grid can be updated without a full-page post-back as the user types in the search box. Notice that I've added a trigger to the panel. This will be fired from JavaScript eventually, but is critical to this working smoothly.
  • <asp:UpdatePanel runat="server" ID="upPersonSearch" UpdateMode="conditional">
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="txtSearchStr" EventName="TextChanged" />
        </Triggers>
        <ContentTemplate>
  • ObjectDataSource - this is the work-horse of the control that handles retrieving the data and passing it to the GridView. The only interesting piece here is setting the default value of the search parameter to some random characters so when the page first loads and the GridView asks for its data, nothing will be passed. Also note that the TextBox control is identified as the SelectParameter.
  • <asp:ObjectDataSource runat="server" ID="odsPeopleList" 
           TypeName="ASPNETPersonSearch.People" SelectMethod="SearchPeople">
        <SelectParameters>
            <asp:ControlParameter ControlID="txtSearchStr" 
                      Name="namecontains" DefaultValue="@@##$$" />
        </SelectParameters>
    </asp:ObjectDataSource>
  • GridView - the results are displayed in a GridView in my example. Of course, you could use any of the data objects in ASP.NET (ListView, DataView, etc.). The GridView is wired up to the ObjectDataSource and identifies PeopleID as the DataKey. This will be important when we get to handling the user selecting a search result.
  • <asp:GridView runat="server" ID="gvPeopleList" 
               DataSourceID="odsPeopleList" AutoGenerateColumns="false" 
               ShowFooter="false" ShowHeader="False" 
               DataKeyNames="PeopleID" >
        <Columns>
            <asp:TemplateField>
                <ItemTemplate>
                <div>
                    <asp:LinkButton runat="server" 
                       style="text-decoration:none;" ID="btn1" 
                       SkinID="plain" CommandName="Select" />
                </div>
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
        <EmptyDataTemplate>
            <asp:Label runat="server" ID="lbl1" />
        </EmptyDataTemplate>
    </asp:GridView>
Let's stop there and examine what we have. On the page, we have a TextBox, an UpdatePanel, an ObjectDataSource and a grid. We could run the page like this and it would work...sort of. The big problem I need to overcome is that the TextChanged property of the TextBox will only fire when the user clicks the <enter> or the <tab> key. But, that's not what I want. I want it to fire as they type. Sigh....JavaScript.

Key Point #4 - The JavaScript

To accomplish that on-the-fly feeling of typing and seeing the results, you need some sort of JavaScript. My first thought was to use the OnKeyUp or OnKeyPress JavaScript functions...and in fact, that is what I tried at first. But there is a problem. If a person types with any type of speed, and you trigger the post-back and update of your data, there is a lag behind what the person types and what search string is submitted. You could wrap all of the JavaScript in a timer function that keeps track of the time between key strokes and resets the clock if the user types some more...but the more I tested that approach, the more awkward it worked. That's when I hit on the following:
  • Start some function OnFocus of the text control, and stop it OnBlur.
  • Then, instead of responding to the user's keystrokes, just trigger the postback every second or so.
Of course, that may seem excessive, so I set a variable to hold the last-used search value. If it matches what is already in the search box (that is, the person has typed a value, the search has triggered, and the they sat there for 20 seconds looking through the names), the JavaScript just skips over the postback, saving us a roundtrip to the server.
Here are the highlights from the JavaScript:
  • Global variables - used as reference in functions. I actually didn't put the prm variable in the PeopleSearch.js file of the project. Instead, I placed it on the User Control designer page. I also wrapped it in logic that inspects whether it already exists - either created for another purpose, or because I have placed two of these user controls on the same page. This is because I needed to reference something in the DOM of a rendered page, rather than create a new object.
  • //global variables defined in the PeopleSearch.vb file
    var intervalTimerId = 0;
    var currenttext = "";
    
    //script reference at the top of the PeopleSearch.ascx page
    <script language="javascript" type="text/javascript">
        //in case there are two User controls on the same page, 
        //we only set this variable once
        if(!prm) {var prm = Sys.WebForms.PageRequestManager.getInstance();}
    </script>
  • StartSearching - get's the party started. There really isn't much to this. Simply tells JavaScript to start executing the doPostBackAsynch function every second. This function is triggered from the TextBox's OnFocus event. When referenced in the TextBox, notice that I set the ID of the control programmatically (with this.id). This is critical to being able to reuse this control anywhere - and multiple times on the same page.
  • function startSearching(cid) {
        intervalTimerId = setInterval ( "doPostBackAsync( '" + cid + "', '' )", 1000 );
    }
  • endSearching - That's enough of that! Kill the interval.
  • function endSearching() {
        clearInterval (intervalTimerId) ;
    }
  • doPostBackAsync - Ah, the magic. First, collect the search string from the text box. Then, make sure the user has input a reasonable-length search string (greater than two) and also that the search string in the text box does not match the global variable currenttext. This is the trick I used to ensure we don't hit a bazillion server calls when someone parks in the text box then goes to get a coffee. Of course, the JavaScript interval will keep firing, but, shoot, that's on the client-side....so, who cares Smile | :)
  • The real good stuff in this function is the following code:
    prm._asyncPostBackControlClientIDs.push(eventName);
    I ran in to this through some Googling and it's genius. This is how to trigger an UpdatePanel from JavaScript. The .push method simulates the __doPostBack event. Here is the full code:
    function doPostBackAsync( eventName, eventArgs )
        {  var tbox = document.getElementById(eventName);
            if(tbox.value.length > 2 && tbox.value != currenttext )
            {
                if( !Array.contains( prm._asyncPostBackControlIDs, eventName) )
                {prm._asyncPostBackControlIDs.push(eventName);}
             
                if( !Array.contains( prm._asyncPostBackControlClientIDs, eventName) )
                {
                    prm._asyncPostBackControlClientIDs.push(eventName);
                }
                __doPostBack( eventName, eventArgs );
                currenttext = tbox.value;
            }
            
        }
OK, we're almost done. The last part is registering all of this JavaScript. I chose to use a ScriptManagerProxy on the designer page of the control. This allows you to reference and register the functions of a .js file where you need it, instead of putting the reference on the consuming .aspx page or worse, in a Master page.
<asp:ScriptManagerProxy runat="server" ID="sm1">
    <Scripts>
        <asp:ScriptReference Path="~/js/EmployeeSearch.js" />
    </Scripts>
</asp:ScriptManagerProxy>

Key Point #5 - The Code-Behind

Now that we've done all this work to allow us to control things on the server-side...let's look at what we can do. First, since this is a user control, it's reasonable to assume that you will want to bind data and pass data back out to the consuming page. So, you have to import the Web.UI and ComponentModel namespaces.
Imports System.Web.UI
Imports System.ComponentModel
Next, let's consider what our results will look like in our GridView. I could just leave it as First Name and Last Name, but that's just boring. Instead, as the row is bound, I chose to get the search string, inspect it for a comma (the person is entering Lastname, Firstname), and then display the results with highlights to show them what the results matched in their search. This seems a lot of work, but it makes the user experience very natural. Check out the looping image at the top of the article again. Notice that when I enter my first name, then last, the results are appearing as I'm typing. Then, when I go to last <comma> first, the results switch to the way I'm entering the search. Also, I use the People object in this method which makes handling the names a snap. Finally, I'm choosing to just show the results as delivered by the People.SearchPeople method. You could easily add pictures, links, or other things about this person in this method to punch up the search results.
Private Sub gvPeopleList_RowDataBound(ByVal sender As Object, _
            ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
            Handles gvPeopleList.RowDataBound
    'retrieve the text entered by the user
    Dim searchStr As String = txtSearchStr.Text

    If e.Row.RowType = DataControlRowType.DataRow Then

        'create new person object
        Dim peep As New Person()

        'when we are done inspecting the input text, 
        'this will hold the string to show users
        'in the result box
        Dim Namestr As String = ""

        'if they person is entering Lastname, Firstname
        Dim strArr As String() = Split(searchStr, ",")

        'if there is a comma in the search text
        If UBound(strArr) > 0 Then  'there was a comma
            'if the Last name text is more than just white space, 
            'show the matches using the Highlight function
            peep.Lastname = IIf(Trim(strArr(0)).Length > 0, _
                 Utilities.Highlight(Trim(strArr(0)), _
                 e.Row.DataItem("LastName")), _
                 e.Row.DataItem("LastName"))

            'if the first name text is more than just white space, 
            'show the matches using the Highlight function
            peep.Firstname = IIf(Trim(strArr(1)).Length > 0, _
                 Utilities.Highlight(Trim(strArr(1)), _
                 e.Row.DataItem("Firstname")), _
                 e.Row.DataItem("Firstname"))

            'set the presentation variable
            Namestr = peep.Lastname & ", " & peep.Firstname
        Else
            'if there was no comma, then search for a space
            strArr = Split(searchStr)
            If UBound(strArr) > 0 Then
                'if there was a space....
                peep.Lastname = IIf(Trim(strArr(1)).Length > 0, _
                     Utilities.Highlight(Trim(strArr(1)), _
                     e.Row.DataItem("LastName")), _
                     e.Row.DataItem("LastName"))
                peep.Firstname = IIf(Trim(strArr(0)).Length > 0, _
                     Utilities.Highlight(Trim(strArr(0)), _
                     e.Row.DataItem("Firstname")), _
                     e.Row.DataItem("Firstname"))
                Namestr = peep.Firstname & " " & peep.Lastname
                'if all the other options fail, just highlight 
                'the First and Last names from the search
                'results and set the presentation variable
            Else
                peep.Firstname = Utilities.Highlight(searchStr, _
                                     e.Row.DataItem("Firstname"))
                peep.Lastname = Utilities.Highlight(searchStr, _
                                     e.Row.DataItem("LastName"))
                Namestr = peep.Lastname & ", " & peep.Firstname
            End If
        End If


        'set the persons location
        peep.Location = e.Row.DataItem("Location")

        'if the person has a job title, set it
        peep.JobTitle = IIf(e.Row.DataItem("JobTitle") _
           Is DBNull.Value, "", e.Row.DataItem("JobTitle"))


        'Find the link button in the grid
        Dim btn As LinkButton = TryCast(e.Row.FindControl("btn1"), LinkButton)

        'set the text of the link button
        btn.Text = "<b>" & Namestr & "</b>" & _
                   "<br />" & peep.JobTitle & _
                   " - " & peep.Location

    ElseIf e.Row.RowType = DataControlRowType.EmptyDataRow Then
        'If there person has entered more than two characters, 
        'show them that there were no search results
        If txtSearchStr.Text.Length > 2 Then
            Dim lbl As Label = TryCast(e.Row.FindControl("lbl1"), Label)
            lbl.Text = "No matching records were found for the search string: <i>" & _
                       searchStr & "</i>."

            'otherwise, do not show the empty row template
        Else
            e.Row.Visible = False
        End If

    End If
End Sub
OK, so the results look pretty. Now, let's think about how we want to handle a user clicking on a result. First, I like setting up a Bindable property that always returns the DataKey PeopleID from the selected row of the GridView. This is a pretty elegant way of passing the selected value of your gvPeopleList GridView to your consuming page. In theory, you could also pass in a value for PeopleID, but I haven't covered that situation in this post.
Private _PeopleID As Integer = 0
<Bindable(True)> <Browsable(True)> _
Public Property PeopleID() As Integer
    Get
        Return CType(gvPeopleList.DataKeys(_
          gvPeopleList.SelectedIndex).Item("PeopleID"), Integer)
    End Get
    Set(ByVal value As Integer)
        _PeopleID = value
    End Set
End Property
We can now tell our consuming page what "value" has been selected, but that's not good enough. We also need to trigger a public event that the consuming page can watch for and react to - as if our User Control were a "normal" ASP control, like a DropDownList's SelectedIndexChanged event. In this case, I've wired up this public event (PersonSelected) to the GridView's SelectedIndexChanged event.
Public Event PersonSelected(ByVal sender As Object, ByVal e As System.EventArgs)

Private Sub gvPeopleList_SelectedIndexChanged(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles gvPeopleList.SelectedIndexChanged
    RaiseEvent PersonSelected(sender, e)
End Sub
Phew. That's it. That is the control. You could go crazy on this code-behind and do all types of fancy things. In fact, I have several ideas for this control. But, as advertised, we now have a User Control we could drop on any page in my solution to search people and return the matching ID. One last thing about the control: I don't like to reference controls from the consuming page's header. Instead, I prefer to add the control to my web.config, like this:
<system.web>
    <pages>
      <controls>
         <add tagPrefix="uc1" 
            src="~/UserControls/PersonSearch.ascx" 
            tagName="PersonSearch"/>
      </controls>
    </pages>
</system.web>

Key Point #6 - Using the Control

If you look at the solution I've attached to this post, you will see there are a lot of files that go in to this control.
  • Classes/People.vb, Classes/Person.vb
  • css/default.css
  • js/PersonSearch.js
  • UserControls/PeopleSearch.ascx, UserControls/PeopleSearch.ascx.vb
That seems like a ton of overhead, but it isn't really. And the best part is, now we can consume this pretty complex collection of items in a "normal" ASPX page without any trouble. Here is a sample designer and code-behind page to "use" this control:
<%@ Page Language="vb" AutoEventWireup="false" 
    CodeBehind="Default.aspx.vb" Inherits="ASPNETPersonSearch._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>
    <link href="css/default.css" rel="stylesheet" type="text/css" />
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <div>
        <table>
            <tr>
                <td>Choose a Person</td>
                <td><uc1:PersonSearch runat="server" id="ps1" /></td>
            </tr>
        </table>
    </div>
    </form>
</body>
</html>
Very clean...right? And the code-behind:
Partial Public Class _Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, _
                  ByVal e As System.EventArgs) Handles Me.Load

    End Sub

    Private Sub ps1_PersonSelected(ByVal sender As Object, _
            ByVal e As System.EventArgs) Handles ps1.PersonSelected
        Dim pid As Integer = ps1.PeopleID
        'do stuff with the selected person ID
    End Sub
End Class
Heck, you could even have an UpdatePanel and use the PersonSelected event as a trigger like this:
<table>
    <tr>
        <td>Choose a Person</td>
        <td>
            <uc1:PersonSearch runat="server" id="ps1" />
        </td>
    </tr>
</table>
<asp:UpdatePanel runat="server" ID="upResult" UpdateMode="conditional">
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID="ps1" EventName="PersonSelected" />
    </Triggers>
    <ContentTemplate>
        <asp:Label runat="server" ID="lblresult" />
    </ContentTemplate>
</asp:UpdatePanel>
You should know that if you just copy the solution attached to this post and try to run it, it will not work. You must change the database reference in the web.config file. If you just try to run this project, you will likely get this error:
The DataSet in data source 'odsPeopleList' does not contain any tables.
This error means that the SQL in your People.SearchPeople method is not filling up a DataSet (rather than returning no matching results). That only happens when there is a problem with the query itself.

Conclusion

There you have it. A really portable, pretty, easy-to-implement search ComboBox using AJAX, ASP.NET and a smidge of JavaScript. One thing I've found while using this control is if you place the control itself in an UpdatePanel, your user will lose focus on the control unless you configure the "parent" UpdatePanel to: ChildrenAsTriggers="false". However, you can throw this control on any .aspx page, or in the <content> of any child page, or heck, even in a master page if you'd like.
One thing I did not cover in this post is styling the results in the grid. I've included a .css file with the sample project, but it doesn't deal with the grid at all.

By www.DrupalStudy.com
Tag :

Socket Programming in .NET using C#

Socket Programming in .NET using C#

Introduction

In this article, we will learn the basics of socket programming in .NET Framework using C#. Secondly, we will create a small application consisting of a server and a client, which will communicate using TCP and UDP protocols.

Pre-requisites

  • Must be familiar with .NET Framework.
  • Should have good knowledge of C#.
  • Basic knowledge of socket programming.

1.1 Networking basics:

Inter-Process Communication i.e. the capability of two or more physically connected machines to exchange data, plays a very important role in enterprise software development. TCP/IP is the most common standard adopted for such communication. Under TCP/IP each machine is identified by a unique 4 byte integer referred to as its IP address (usually formatted as 192.168.0.101). For easy remembrance, this IP address is mostly bound to a user-friendly host name. The program below (showip.cs) uses the System.Net.Dns class to display the IP address of the machine whose name is passed in the first command-line argument. In the absence of command-line arguments, it displays the name and IP address of the local machine.
using System;
using System.Net;
class ShowIP{
    public static void Main(string[] args){
        string name = (args.Length < 1) ? Dns.GetHostName() : args[0];
        try{
            IPAddress[] addrs = Dns.Resolve(name).AddressList;
            foreach(IPAddress addr in addrs) 
                Console.WriteLine("{0}/{1}",name,addr);
        }catch(Exception e){
            Console.WriteLine(e.Message);
        }
    }
}
Dns.GetHostName() returns the name of the local machine and Dns.Resolve() returns IPHostEntry for a machine with a given name, the AddressList property of which returns the IPAdresses of the machine. The Resolve method will cause an exception if the mentioned host is not found.
Though IPAddress allows to identify machines in the network, each machine may host multiple applications which use network for data exchange. Under TCP/IP, each network oriented application binds itself to a unique 2 byte integer referred to as its port-number which identifies this application on the machine it is executing. The data transfer takes place in the form of byte bundles called IP Packets or Datagrams. The size of each datagram is 64 KByte and it contains the data to be transferred, the actual size of the data, IP addresses and port-numbers of sender and the prospective receiver. Once a datagram is placed on a network by a machine, it will be received physically by all the other machines but will be accepted only by that machine whose IP address matches with the receiver’s IP address in the packet. Later on, this machine will transfer the packet to an application running on it which is bound to the receiver’s port-number present in the packet.
TCP/IP suite actually offers two different protocols for data exchange. The Transmission Control Protocol (TCP) is a reliable connection oriented protocol while the User Datagram Protocol (UDP) is not very reliable (but fast) connectionless protocol.

1.2 Client-Server programming with TCP/IP:

Under TCP there is a clear distinction between the server process and the client process. The server process starts on a well known port (which the clients are aware of) and listens for incoming connection requests. The client process starts on any port and issues a connection request.
The basic steps to create a TCP/IP server are as follows:
  1. Create a System.Net.Sockets.TcpListener with a given local port and start it:
    TcpListener listener = new TcpListener(local_port);
    listener.Start();
  2. Wait for the incoming connection request and accept a System.Net.Sockets.Socket object from the listener whenever the request appears:
    Socket soc = listener.AcceptSocket(); // blocks
  3. Create a System.Net.Sockets.NetworkStream from the above Socket:
    Stream s = new NetworkStream(soc);
  4. Communicate with the client using the predefined protocol (well established rules for data exchange):
  5. Close the Stream:
    s.Close();
  6. Close the Socket:
    s.Close();
  7. Go to Step 2.
Note when one request is accepted through step 2 no other request will be accepted until the code reaches step 7. (Requests will be placed in a queue or backlog.) In order to accept and service more than one client concurrently, steps 2 – 7 must be executed in multiple threads. Program below (emptcpserver.cs) is a multithreaded TCP/IP server which accepts employee name from its client and sends back the job of the employee. The client terminates the session by sending a blank line for the employee’s name. The employee data is retrieved from the application’s configuration file (an XML file in the directory of the application and whose name is the name of the application with a .config extension).
using System;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Configuration;

class EmployeeTCPServer{
    static TcpListener listener;
    const int LIMIT = 5; //5 concurrent clients
    
    public static void Main(){
        listener = new TcpListener(2055);
        listener.Start();
        #if LOG
            Console.WriteLine("Server mounted, 
                            listening to port 2055");
        #endif
        for(int i = 0;i < LIMIT;i++){
            Thread t = new Thread(new ThreadStart(Service));
            t.Start();
        }
    }
    public static void Service(){
        while(true){
            Socket soc = listener.AcceptSocket();
            //soc.SetSocketOption(SocketOptionLevel.Socket,
            //        SocketOptionName.ReceiveTimeout,10000);
            #if LOG
                Console.WriteLine("Connected: {0}", 
                                         soc.RemoteEndPoint);
            #endif
            try{
                Stream s = new NetworkStream(soc); 
                StreamReader sr = new StreamReader(s);
                StreamWriter sw = new StreamWriter(s);
                sw.AutoFlush = true; // enable automatic flushing
                sw.WriteLine("{0} Employees available", 
                      ConfigurationSettings.AppSettings.Count);
                while(true){
                    string name = sr.ReadLine();
                    if(name == "" || name == null) break;
                    string job = 
                        ConfigurationSettings.AppSettings[name];
                    if(job == null) job = "No such employee";
                    sw.WriteLine(job);
                }
                s.Close();
            }catch(Exception e){
                #if LOG
                    Console.WriteLine(e.Message);
                #endif
            }
            #if LOG
                Console.WriteLine("Disconnected: {0}", 
                                        soc.RemoteEndPoint);
            #endif
            soc.Close();
        }
    }
}
Here is the content of the configuration file (emptcpserver.exe.config) for the above application:
<configuration>
    <appSettings>
        <add key = "john" value="manager"/> 
        <add key = "jane" value="steno"/>
        <add key = "jim" value="clerk"/>
        <add key = "jack" value="salesman"/>
    </appSettings>
</configuration>
The code between #if LOG and #endif will be added by the compiler only if the symbol LOG is defined during compilation (conditional compilation). You can compile the above program either by defining the LOG symbol (information is logged on the screen):
  • csc /D:LOG emptcpserver.cs
or without the LOG symbol (silent mode):
  • csc emptcpserver.cs
Mount the server using the command start emptcpserver.
To test the server you can use: telnet localhost 2055.
Or, we can create a client program. Basic steps for creating a TCP/IP client are as follows:
  1. Create a System.Net.Sockets.TcpClient using the server’s host name and port:
    TcpClient client = new TcpClient(host, port);
  2. Obtain the stream from the above TCPClient.
    Stream s = client.GetStream()
  3. Communicate with the server using the predefined protocol.
  4. Close the Stream:
    s.Close();
  5. Close the connection:
    client.Close();
The program below (emptcpclient.cs) communicates with EmployeeTCPServer:
using System;
using System.IO;
using System.Net.Sockets;

class EmployeeTCPClient{
    public static void Main(string[] args){
        TcpClient client = new TcpClient(args[0],2055);
        try{
            Stream s = client.GetStream();
            StreamReader sr = new StreamReader(s);
            StreamWriter sw = new StreamWriter(s);
            sw.AutoFlush = true;
            Console.WriteLine(sr.ReadLine());
            while(true){
                Console.Write("Name: ");
                string name = Console.ReadLine();
                sw.WriteLine(name);
                if(name == "") break;
                Console.WriteLine(sr.ReadLine());
            }
            s.Close();
        }finally{
            // code in finally block is guranteed 
            // to execute irrespective of 
            // whether any exception occurs or does 
            // not occur in the try block
            client.Close();
        } 
    }
}

1.3 Multicasting with UDP

Unlike TCP, UDP is connectionless i.e. data can be send to multiple receivers using a single socket. Basic UDP operations are as follows:
  1. Create a System.Net.Sockets.UdpClient either using a local port or remote host and remote port:
    UdpClient client = new UdpClient(local_ port);
    or
    UdpClient client = new UdpClient(remote_host, remote_port);
  2. Receive data using the above UdpClient:
    System.Net.IPEndPoint ep = null;
    byte[] data = client.Receive(ref ep);
    byte array data will contain the data that was received and ep will contain the address of the sender.
  3. Send data using the above UdpClient.. If the remote host name and the port number have already been passed to the UdpClient through its constructor, then send byte array data using:
    client.Send(data, data.Length);
    Otherwise, send byte array data using IPEndPoint ep of the receiver:
    client.Send(data, data.Length, ep);
The program below (empudpserver.cs) receives the name of an employee from a remote client and sends it back the job of that employee using UDP:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Configuration;

class EmployeeUDPServer{
    public static void Main(){
        UdpClient udpc = new UdpClient(2055);
        Console.WriteLine("Server started, servicing on port 2055");
        IPEndPoint ep = null;
        while(true){
            byte[] rdata = udpc.Receive(ref ep);
            string name = Encoding.ASCII.GetString(rdata);
            string job = ConfigurationSettings.AppSettings[name];
            if(job == null) job = "No such employee";
            byte[] sdata = Encoding.ASCII.GetBytes(job);
            udpc.Send(sdata,sdata.Length,ep);
        }
    }
}
Here is the content of the configuration file (empudpserver.exe.config) for above application:
<configuration>
    <appSettings>
        <add key = "john" value="manager"/> 
        <add key = "jane" value="steno"/>
        <add key = "jim" value="clerk"/>
        <add key = "jack" value="salesman"/>
    </appSettings>
</configuration>
The next program (empudpclient.cs) is a UDP client to the above server program:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class EmployeeUDPClient{
    public static void Main(string[] args){
        UdpClient udpc = new UdpClient(args[0],2055);
        IPEndPoint ep = null;
        while(true){
            Console.Write("Name: ");
            string name = Console.ReadLine();
            if(name == "") break;
            byte[] sdata = Encoding.ASCII.GetBytes(name);
            udpc.Send(sdata,sdata.Length);
            byte[] rdata = udpc.Receive(ref ep);
            string job = Encoding.ASCII.GetString(rdata);
            Console.WriteLine(job);
        }
    }
}
UDP also supports multicasting i.e. sending a single datagram to multiple receivers. To do so, the sender sends a packet to an IP address in the range 224.0.0.1 – 239.255.255.255 (Class D address group). Multiple receivers can join the group of this address and receive the packet. The program below (stockpricemulticaster.cs) sends a datagram every 5 seconds containing the share price (a randomly calculated value) of an imaginary company to address 230.0.0.1:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class StockPriceMulticaster{
    static string[] symbols = {"ABCD","EFGH", "IJKL", "MNOP"};
    public static void Main(){
        UdpClient publisher = new UdpClient("230.0.0.1",8899);
        Console.WriteLine("Publishing stock prices to 230.0.0.1:8899");
        Random gen = new Random();
        while(true){
            int i = gen.Next(0,symbols.Length);
            double price = 400*gen.NextDouble()+100;
            string msg = String.Format("{0} {1:#.00}",symbols,price);
            byte[] sdata = Encoding.ASCII.GetBytes(msg);
            publisher.Send(sdata,sdata.Length);
            System.Threading.Thread.Sleep(5000);
        }
    }
}
Compile and start stockpricemulticaster.
The next program (stockpricereceiver.cs) joins the group of address 230.0.0.1, receives 10 stock prices and then leaves the group:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
class StockPriceReceiver{
    public static void Main(){
        UdpClient subscriber = new UdpClient(8899);
        IPAddress addr = IPAddress.Parse("230.0.0.1");
        subscriber.JoinMulticastGroup(addr);
        IPEndPoint ep = null;
        for(int i=0; i<10;i++){
            byte[] pdata = subscriber.Receive(ref ep);
            string price = Encoding.ASCII.GetString(pdata);
            Console.WriteLine(price);
        }
        subscriber.DropMulticastGroup(addr);
    }
}
Compile and run stockpricereceiver.
source : ccodeproject.com
By www.DrupalStudy.com
Tag :

Follow By Email

About Me

My name is Abdennacer
a 24 year old Freelancer Web.
-Web Developer
-SEO professional
-Software programer

Read More =>>

Contact Me

Name

Email *

Message *

Email/Call Me

Email: devnasser@gmail.com
Phone: +212 676 47 95 81

Stats and Pageviews

@All rights reserved Copy | © .:* Drupal Study *:.