LINQ To SQL

Introduction

LINQ is one of the most important features in .NET Framework 3.5 (Visual Studio 2008). It's the new way to mapping database tables to classes, and as we know, we call this O/R Mapping. An article on how to write LINQ code quickly is always welcome for beginners, and I think that reading samples is the best way to learn a new technique.

These are samples created while I was learning and using LINQ, and I want to share them now. Hope they will be helpful. I will use Northwind database as a sample, which you can download from the link at the top of this article.

I recommend that you read 101 LINQ Samples if you would like to learn more.


// Basic
// Select * From Products
var query1 = from p in db.Products select p;
// Select ProductID, ProductName, UnitPrice From Products
var query2 = from p in db.Products select new
{
p.ProductID, p.ProductName, p.UnitPrice
};

Note: query2 will create a new class which contains three properties that map the ProductId, ProductName, and UnitPrice.

// Where // Select * From Products Where ProductID = 1
var query3 = from p in db.Products where p.ProductID == 1 select p;

// Select * From Products Where SupplierId =5 and UnitPrice > 20
var query4 = from p in db.Products where p.SupplierID == 5 && p.UnitPrice > 20 select p;

// Select * From Products Where SupplierId =5 Or SupplierId=6
var query5 = from p in db.Products where p.SupplierID == 5 || p.SupplierID == 6 select p;

Note: The condition in the where block is a logical express, a boolean value is returned just like in if().

// Order By
// Select * From Products Order By ProductId
var query6 = from p in db.Products
orderby p.ProductID
select p;

// Select * From Products Order By ProductId Desc
var query7 = from p in db.Products
orderby p.ProductID descending
select p;

// Select * From Products Order By CategoryId, UnitPrice Desc
var query8 = from p in db.Products
orderby p.CategoryID, p.UnitPrice descending
select p;

Note: The default order is ascending, the order by p.ProductID is same as order by p.ProductID ascending, just like in T-SQL.


// Top
// Select Top 10 * From Products
var query9 = (from p in db.Products
select p).Take(10);

// Select Top 1 * From Products
var query10 = (from p in db.Products
select p).Take(1);
// or
var query11 = (from p in db.Products
select p).First();

Note: If it just returns one record, I recommend using First instead of Take(1).

// Top with Order By
// Select Top 10 * From Products Order By ProductId
var query12 = (from p in db.Products
orderby p.ProductID
select p).Take(10);

// Distinct
// Select Distinct CategoryId From Products
var query13 = (from p in db.Products
select p.CategoryID).Distinct();

// Group By
// Select CategoryId, Count(CategoryID) As NewField
// From Products Group By CategoryId
var query14 = from p in db.Products
group p by p.CategoryID into g
select new {
CategoryId = g.Key,
NewField = g.Count()
};

// Select CategoryId, Avg(UnitPrice) As NewField From Products Group By CategoryId
var query15 = from p in db.Products
group p by p.CategoryID into g
select new {
CategoryId = g.Key,
NewField = g.Average(k => k.UnitPrice)
};

// Select CategoryId, Sum(UnitPrice) As NewField From Products Group By CategoryId
var query16 = from p in db.Products
group p by p.CategoryID into g
select new {
CategoryId = g.Key,
NewField = g.Sum(k => k.UnitPrice )
};

// Union
// Select * From Products Where CategoryId =1 union Select *
// From Products Where CategoryId = 2
var query17 = (from p in db.Products
where p.CategoryID == 1
select p).Union(
from m in db.Products
where m.CategoryID == 2
select m
);

// Two tables
// Select A.ProductId, A.ProductName, B.CategoryId, B.CategoryName
// From Products A, Categories B
// Where A.CategoryID = B.CategoryID and A.SupplierId =1
var query18 = from p in db.Products
from m in db.Categories
where p.CategoryID == m.CategoryID && p.SupplierID == 1
select new {
p.ProductID,
p.ProductName,
m.CategoryID,
m.CategoryName
};


Language Integrated Query (LINQ) to SQL

Introducing LINQ to Relational Data

LINQ introduced the concept of a unified data access technique to eliminate challenges of accessing data from a disparity of sources. LINQ has been extended to include the ability to access relational data through LINQ to SQL and LINQ to Entities. LINQ to SQL offers a direct mapping to the Microsoft SQL Server family of databases and allows you to query and manipulate objects associated with database tables. LINQ to Entities offers a more flexible mapping to relational data stored in other databases beyond just SQL Server. You build against a conceptual schema that is mapped to an actual schema. The remainder of this article will concentrate on LINQ to SQL.

LINQ to SQL Object Model

LINQ to SQL allows you to query and manipulate objects associated with database tables. Creating an object model involves creating a typed object that represents the database connection along with classes that map to database entities. The following list of objects are involved:

There are a few different ways to create an object model. The two most straight forward approaches are to use the provided designer within Visual Studio 2008 to auto-generate the objects and mappings or adding attributes to existing objects. For this example I downloaded the Northwind sample database and loaded it in my SQL Server so that I could retrieve data from it for testing. I created a new solution and added a console application. I then added a LINQ to SQL data class to the console application. I used the Server Explorer to setup a new connection to the Northwind database my local SQL Server, and then dragged all of the tables from the Server Explorer on to the design surface for the data class. Finally I right clicked on the design surface and chose "Layout Diagram" to auto arrange all of the newly added tables and relationships. Refer to Figure 1 for an example of the completed design surface


Test Driving LINQ to SQL through Examples

Now that we've covered the background let's use a couple of examples of LINQ to SQL. Our examples will demonstrate the use of the DataContext object along with an example of querying the Northwind sample database using expressions.

Querying Data

The following example code uses LINQ syntax to query data from the database and then loop through it to print it to the console. You'll notice how the query expression syntax is the same as what you would find whether it be LINQ to XML or another form of LINQ. Also notice how the NorthwindDataClassesDataContext object is wrapped within a using construct. The NorthwindDataClassesDataContext is an object that extends the DataContext class and represents a strongly typed connection to our database. The NorthwindDataClassesDataContext is a disposable type, so wrapping it in a using block ensures it is properly disposed after use.

using (NorthwindDataClassesDataContext context = new NorthwindDataClassesDataContext()) {
var results = from customers in context.Customers where customers.City == "London" orderby customers.CompanyName select customers;
foreach (var customer in results)
{
Console.WriteLine("Company is {0} and contact is {1}", customer.CompanyName, customer.ContactName
);
} // Pause to see the output Console.ReadLine(); }

Modifying Data Using the Created Object Model

The previous example focused on the retrieval of data. We'll now examine the basic data manipulation, create, update, and delete operations and how LINQ to SQL makes them simple. The object model used to manipulate the data is the model generated in one of the earlier sections. After each operation we'll use the SubmitChanges() method to save the changes to the database. We'll execute some simple query expressions with the Count() method to verify the modifications were made as desired. As the example will show, you can use instance of objects, make modifications to them, and the changes that you make are tracked in a change set and pushed back to the database.

using (NorthwindDataClassesDataContext context = new NorthwindDataClassesDataContext()) {
// Add a new record and verify it exists through Count
var customer = new Customer()
{
CompanyName = "Drama Cafe", CustomerID = "DRACA", ContactName = "Tom Smith", City = "Beverly Hills", Address = "123 Melrose Place", PostalCode = "90210"
};
context.Customers.InsertOnSubmit(customer);
context.SubmitChanges();
Console.WriteLine("Number of DRACA records: {0}", context.Customers.Where(c => c.CustomerID == "DRACA").Count()); // Modify the record and verify it is changed through Count customer.ContactName = "Joe Smith";
context.SubmitChanges();
Console.WriteLine("Number of Joe Smith records: {0}", context.Customers.Where(c => c.ContactName == "Joe Smith").Count()); // Delete a record and verify it is removed through Count context.Customers.DeleteOnSubmit(customer);
context.SubmitChanges();
Console.WriteLine("Number of DRACA records: {0}", context.Customers.Where(c => c.CustomerID == "DRACA").Count());
// Pause to see the output Console.ReadLine();

Calling Stored Procedures

The previous retrieve, create, update, and delete operations involved dynamically generated SQL statements. It is also possible to call stored procedures in place of dynamically generated SQL. The sample code below demonstrates how to call stored procedures. The Northwind database has a stored procedure called "Ten Most Expensive Products" we will use for this example. Once again using the Server Explorer navigate to the Stored Procedures in the tree view and drag the stored procedure to the design surface. You'll want to right click on the design surface and select "Show Methods Pane" if it isn't already available. You should now see a method, Ten_Most_Expensive_Products(), that has been created and we'll use in our next code example to get the list of top products and display the prices to the console.

using (NorthwindDataClassesDataContext context = new NorthwindDataClassesDataContext()) {
// Use the ten most expensive products stored procedure
var results = from products in context.Ten_Most_Expensive_Products() select products;
foreach
(var product in results)
{
Console.WriteLine("Product price is {0}", product.UnitPrice);
} // Pause to see the output Console.ReadLine();

Scheduling and Event Logging Windows Service

In previous post we had seen how to create a windows service. In this part we will see how can we log events in windows service and how to schedule service to occur at specific interval.



First we will see how we can Log events in EventViewer event of Windows Service. Steps are as follows.

- Add an EventLog to your windows service class, from Toolbox.

- Give an ID say "MyEventLog1", Set Log to "MyEventLog" and Source to "MyEventLogSource"



This will help you to differentiate your log in event viewer. And you can also create a new view of your event source log. Create new Log View on application and rename it to "MyEventLogSource". Now clear logs, then each new entry with log source "MyEventLogSource" will be logged in the new view.



Now how will we write a new log entry in Windows Service.

Write the following code in OnStart Code block of your Service.



MyEventLog1.WriteEntry("My Windows Service Started.......");



This will be write a log entry. But make sure that you write this code in try-catch block, because on some OS, Windows Service do not have permission to log events. At that time your service will generate an unhandeled exception and it will stop immediately and you will unable to identify the problem that why your service is stopped immediately after it starts.





There can be two types of windows service.

1) Windows service that run only once. There is no any scheduling of the service. If you want to run it again the you must start the service again. It will stop automatically after it complete its job.



2) Windows service that run at specific interval. This service is useful to run Insert command from one server to other backup server. Or it can be use to complete tasks that are added in Database.



The first type of Windows service is very easy. Just put your code in OnStart event of the Windows Service.




Scheduling Windows Service with Timer Control

For scheduling a Windows service declare two global variables in your service class (Make sure not in Service.designer.cs Designer class)
This timer control is not System.Timer, this control is
System.Threading.Timer



private Timer stateTimer;

private TimerCallback timerDelegate;



Timer is useful for creating the scheduling the Service. TimerCallback is useful for specifying the function which will be called when timer interval ends(Tick event fires).



Now write this code in your OnStart block.

timerDelegate = new TimerCallback(DoSomething);
stateTimer = new Timer(timerDelegate, null, 0, 5000); // 5000 = 5 secs

In above code the timerDelegate is a callback function which will specify the Function that will be called each time your timer fires tick event.

Now the stateTimer is assigned to a new timer object which specify timerDelegate, initial delay and interval. In above example '0' is initial delay which specify after timer object is created how much to delay for firing first Tick event. '5000' this specify the interval, this indicate the delay of firing the tick event since the last tick event is fired. Both Initial delay and Interval are specified in milisecond.


Now we will see how to create the function.

protected void DoSomething(object sender)
{
MyEventLog1.WriteEntry("Windows Service event fired at " + DateTime.Now.ToString());
}

Above I have specified the function to be fired at tick event of timer. It will log in EventViewer with date and time.


In next part we will see how can we add a configuration file to the Windows Service, how to install the service and common problem while using with Windows Server 2003 system.

Resizing Data Controls in ASP.NET Applications

Introduction

This article explains about resizing web controls in runtime. In browser output, usually resizing is achieved during Development for fixing the control’s width and height. But sometimes we require resizing the DataGrid, DataList or other data controls to adjust for clearly reading the displayed data. It is rare to see the internet page's output to be adjustable, but it is possible with a few lines of JavaScript.

Event Types

The whole resize logic (column width increase or decrease) depends upon catching the co-ordinate where the user drags the column to be resized. Mouse capturing will be set on initially when the page loads. Events to be captured are the following:

· Mouse Over

· Mouse Down

· Mouse Up

Style Settings

Resizing can be done only on the column headers. Usually all data controls, like DataGrid, GridView or DataList, will be set with some style class for the header row. Here we take advantage of this style for tracking the user's mouse over an event on the header row. This event will initiate the trigger to start capturing the further events like mouse down and mouse up. There is no special style setting to be done other than the user’s look and feel styles. The JavaScript just requires a style name and that is it!

Mouse Capturing

The user will be automatically seeing the difference in the cursor which will signal to them that the DataGrid/GridView is ready for resizing. This will be done by the handler which we have set for document onmousemove. Whenever the user reaches the header row, the first level job is over and mouse down event will be ready to be raised for checking where the user clicks the mouse to start dragging. At this moment the mouse move event will again take over. Now the JavaScript will start handling mousedown and mousemove, which is equal to mouse drag. On the mouse drag, the column size and the table size will increase or decrease based on the mouse’s left or right movement respectively. Whenever the user makes the mouse go up (stops dragging and releasing the mouse), the resizing will get stopped and the current drag object will be destroyed.

More Technical Information on Events


There are some things to look into in the code for a clearer understanding. The column resize can be pulled into the picture by just adding the JavaScript file to the web page. Before that, define the header columns you use in the web pages to the JavaScript file (headerStyle).

Listing 1

var isDraggable = false;
var xX=0;anchorChild=0;anchorMain=0;
var objOn=null;gridMain=null;
var lastObj=undefined;
var headerStyle="DataGrid-HeaderStyle";

Other common settings to be made are the following:

isDraggable – This variable will be initially set to false to denote that dragging logic should not be called; this will become true whenever the mouse hovers over the datagrid’s header column’s border.

xX, anchorChild, anchorMain – These three variables will hold 0 initially on every mouse down event when the user starts dragging. xX will hold the x-axis position of the mouse, anchorChild will hold the width of the Current Resizing cell, and anchorMain will hold the width of the entire DataGrid or control.

objOn – This will hold the current object which is under dragging.

lastObj – This temp object loads the objOn object. This will be captured on mousedown and loaded to objOn during MouseMove.

Firefox – DOM Expressions differ from browser to browser, so we have a common variable which will be checked in every event. When the script is initialized, we initialize the FireFox by finding whether the current browser is Firefox or other.

Listing 2

var firefox = document.getElementById&&!document.all; 

Mouse Events

OnMouseMove – This event is used to capture the current place in which the mouse is hovered and also to change the width of the cell and DataGrid while they drag. So first we will capture the object on which we hover the mouse.

Listing 3

objOn=(!firefox)?e.srcElement:e.target; 

The line below will be used to load the last mouse over the object for reference.

Listing 4

if(lastObj!=undefined)objOn=lastObj; 

Then we have to change the cursor to resizable if the mouse is hovered on the DataGrid header column’s borders.

Listing 5

document.body.style.cursor =(objOn.offsetWidth-e.offsetX < 10 &&
objOn.parentElement.className == headerStyle)?'e-resize':'default';

Now, we decide whether we should start dragging or not. It depends on whether the user has clicked the mouse for dragging which would have made the boolean isDraggable true.

Listing 6

if(isDraggable)
{ document.body.style.cursor='e-resize';
//Current_MouseX_Position - MouseX_DragStart + Cell_Width
ic=e.clientX-xX+anchorChild;
//Current_MouseX_Position - MouseX_DragStart + Table_Width
it=e.clientX-xX+anchorMain;
if(ic>0)
{
gridMain.style.width = it + "px"; //Increase Table Width
objOn.style.width = ic + "px"; //Increase Cell Width
}
}

The above piece of code will increase or decrease the size of the DataGrid based on the mouse movement.

OnMouseDown – This event is used to capture the point or pixel where the user has started to drag (if the user has the mouse pointer over the DataGrid header’s border). Here we will find the Cell’s Offset Position and Table’s Offset position which will give the current X-Axis width of the cell and the table respectively.

First we will capture the object over which we hover the mouse.

Listing 7

objOn=(!firefox)?e.srcElement:e.target; 

Then we have to check whether the mouse is hovered on the DataGrid header column’s borders. If it is true, the following steps will be carried out.

Listing 8

if(objOn.parentElement.className == headerStyle &&
document.body.style.cursor=='e-resize')
{

Set the Boolean Flag isDraggable to denote that we are in draggable mode.

Listing 9

isDraggable = true; 

Set the gridMain object with the DataGrid or GridView object.

Listing 10

gridMain = objOn.parentElement.parentElement.parentElement; 

Get the object of main table which corresponds to current column's width variation.

Listing 11

xX = e.clientX; 

Get the current Mouse X Position.

Listing 12

anchorChild = objOn.offsetWidth; 

Getting the current Cell/Column Width

Listing 13

anchorMain = gridMain.offsetWidth; 

Get the current Table Width.

Listing 14

lastObj = objOn; 

This will be the else part of the Current IF Statement. If the mouse is clicked outside the header scope, the lastObj variable is set to undefined.

Listing 15

lastObj = undefined;

OnMouseUp – This event is used to stop the dragging. There is no specific code to stop the drag; the drag event will be fired based on mouseover and mousedown events. When the mouse click is up, lastObj object used in those events will be undefined and isDraggable boolean flag will be set to false to say that the drag has been stopped by the user.

Dragging Stop and Forget LastObj

Listing 16

isDraggable = false;
lastObj=undefined;

Here is full JavaScript Code

var firefox = document.getElementById&&!document.all; //Check whether the browser is FF;
var isDraggable = true;
var xX=0;anchorChild=0;anchorMain=0;
var objOn=null;gridMain=null;
var lastObj=undefined;
var headerStyle="DataGrid-HeaderStyle";

document.onmouseup=mouseUpCapture;

document.onmousemove=mouseMoveCapture;

document.onmousedown=mouseDownCapture;

/*EventHandler to capture Mouse Up*/

function mouseUpCapture(){isDraggable = false;lastObj=undefined;} //Dragging Stop and Forget LastObj;


/*EventHandler to capture Mouse Down*/

function mouseDownCapture(e){

if(!firefox)e = event;objOn=(!firefox)?e.srcElement:e.target; //Check whether the browser is FF and set event object accordingly;

//To Check whether the mouse hovers over the HeaderRow and cursor type is currently re-sizable;

if(objOn.parentElement.className == headerStyle && document.body.style.cursor=='e-resize')
{

isDraggable = true; //Start understanding that we can start dragging;
gridMain = objOn.parentElement.parentElement.parentElement; //Getting the object of main table which corresponds to current column's width variation;


xX = e.clientX; //Getting the current Mouse X Position;

anchorChild = objOn.offsetWidth; //Getting the current Cell/Column Width;

anchorMain = gridMain.offsetWidth; //Getting the current Table Width;

lastObj = objOn; //Stores the current mouse hovering object for moving reference;

}

else{lastObj = undefined;}
}

/*EventHandler to capture Mouse Move*/

function mouseMoveCapture(e){

if(!firefox)e = event; //Check whether the browser is FF and set event object accordingly;
objOn=(!firefox)?e.srcElement:e.target;

if(lastObj!=undefined)objOn=lastObj; //Loads the mouse hovered object for moving reference;

//Check whether the mouse hovers over the Headerrow's column borders and Make the cursor re-sizable;
document.body.style.cursor =(objOn.offsetWidth-e.offsetX < 10 && objOn.parentElement.className == headerStyle)?'e-resize':'default';

//Start Dragging
if(isDraggable)
{ document.body.style.cursor='e-resize'; //Maintaing the cursor as re-sizable
ic=e.clientX-xX+anchorChild;

//Current_MouseX_Position - MouseX_DragStart + Cell_Width;
it=e.clientX-xX+anchorMain;

//Current_MouseX_Position - MouseX_DragStart + Table_Width;

if(ic>0)
{
gridMain.style.width = it + "px"; //Increase Table Width;
objOn.style.width = ic + "px"; //Increase Cell Width;
}

}

}

The New LinkedList Class and Iterator Class in C#

we need a header field and a constructor method to instantiate the list.

public class LinkedList()
{
private Node header;
public LinkedList()
{

header = new Node("header");
}
public bool IsEmpty() {
return (header.Link == null);
}
public Node GetFirst() {
return header;
}
public void ShowList() {
Node current = header.Link;
while (!(current == null)) {
Console.WriteLine(current.Element);
current = current.Link;
}
}
}


Demonstrating the Iterator Class

Using the Iterator class, it’s easy to write an interactive program to move through a linked list. This also gives us a chance to put all the code for both the Iterator class and the LinkedList class in one place.

using System;
public class Node
{
public Object Element;
public Node Link;
public Node()
{
Element = null;
Link = null;
}
public Node(Object theElement)
{
Element = theElement;
Link = null;
}
}
public class InsertBeforeHeaderException : System.
ApplicationException
{
public InsertBeforeHeaderException(string message) :
base(message)
{
}
}
public class LinkedList
{
private Node header;
public LinkedList()
{
header = new Node("header");
}
public bool IsEmpty()
{
return (header.Link == null);
}
public Node GetFirst()
{
return header;
}
public void ShowList()
{
Node current = header.Link;
while (!(current == null))
{
Console.WriteLine(current.Element);
current = current.Link;
}
}
}
public class ListIter
{
private Node current;
private Node previous;
LinkedList theList;
public ListIter(LinkedList list)
{
theList = list;
current = theList.GetFirst();
previous = null;
}
public void NextLink()
{
previous = current;
current = current.Link;
}
public Node GetCurrent()
{
return current;
}
public void InsertBefore(Object theElement)
{
Node newNode = new Node(theElement);
if (previous.Link == null)
throw new InsertBeforeHeaderException
("Can't insert here.");
else {
newNode.Link = previous.Link;
previous.Link = newNode;
current = newNode;
}
}
public void InsertAfter(Object theElement)
{
Node newNode = new Node(theElement);
newNode.Link = current.Link;
current.Link = newNode;
NextLink();
}
public void Remove()
{
previous.Link = current.Link;
}
public void Reset()
{

current = theList.GetFirst();
previous = null;
}
public bool AtEnd() {
return (current.Link == null);
}
}
class chapter
{
static void Main()
{
LinkedList MyList = new LinkedList();
ListIter iter = new ListIter(MyList);
string choice, value;
try
{
iter.InsertAfter("David");
iter.InsertAfter("Mike");
iter.InsertAfter("Raymond");
iter.InsertAfter("Bernica");
iter.InsertAfter("Jennifer");
iter.InsertBefore("Donnie");
iter.InsertAfter("Michael");
iter.InsertBefore("Terrill");
iter.InsertBefore("Mayo");
iter.InsertBefore("Clayton");
while (true)
{
Console.WriteLine("(n) Move to next node");
Console.WriteLine("(g)Get value in current node");
Console.WriteLine("(r) Reset iterator");
Console.WriteLine("(s) Show complete list");
Console.WriteLine("(a) Insert after");
Console.WriteLine("(b) Insert before");
Console.WriteLine("(c) Clear the screen");
Console.WriteLine("(x) Exit");
Console.WriteLine();
Console.Write("Enter your choice: ");
choice = Console.ReadLine();
choice = choice.ToLower();
char[] onechar = choice.ToCharArray();
switch(onechar[0])
{
case 'n' :
if (!(MyList.IsEmpty()) &&
(!(iter.AtEnd())))
iter.NextLink();
else
Console.WriteLine("Can' move to
next link.");
break;
case 'g' :
if (!(MyList.IsEmpty()))
Console.WriteLine("Element: " +
iter.GetCurrent().Element);
else
Console.WriteLine ("List is empty.");
break;
case 'r' :
iter.Reset();
break;
case 's' :
if (!(MyList.IsEmpty()))
MyList.ShowList();
else
Console.WriteLine("List is empty.");
break;
case 'a' :
Console.WriteLine();
Console.Write("Enter value to insert:");
value = Console.ReadLine();
iter.InsertAfter(value);
break;
case 'b' :
Console.WriteLine();
Console.Write("Enter value to insert:");
value = Console.ReadLine();
iter.InsertBefore(value);
break;
case 'c' :
// clear the screen
break;
case 'x' :
// end of program
break;
}
}
}
catch (InsertBeforeHeaderException e)
{
Console.WriteLine(e.Message);
}
}
}

GENERIC LINKED LIST CLASS AND THE GENERIC NODE CLASS
The System.Collections.Generic namespace provides two generic classes for building linked lists: the LinkedList class and the LinkedListNode class. The Node class provides two data fields for storing a value and a link, whereas the LinkedList class implements a doubly linked list with methods for inserting before a node as well as inserting after a node. The class also provides method for removing nodes, finding the first and last nodes in the linked list, as well as other useful methods.

Generic Linked List Example

LinkedListNode node1 = new LinkedListNode_(“Raymond");
LinkedList names = new LinkedList();

From here, it’s just a matter of using the classes to build and use a linked list. A simple example demonstrates how easy it is to use these classes:

using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main(string[] args)
{
LinkedListNode node = new
LinkedListNode("Mike");
LinkedList names = new LinkedList();
names.AddFirst(node);
LinkedListNode node1 = new
LinkedListNode ("David");
names.AddAfter(node, node1);
LinkedListNode node2 = new
LinkedListNode ("Raymond");
names.AddAfter(node1, node2);
LinkedListNode node3 = new LinkedListNode (null);
LinkedListNode aNode = names.First;
while(aNode != null)
{
Console.WriteLine(aNode.Value);
aNode = aNode.Next;
}
aNode = names.Find("David");
if (aNode != null) aNode = names.First;
while (aNode != null)
{
Console.WriteLine(aNode.Value);
aNode = aNode.Next;
}
Console.Read()
}
}

The linked list in this example does not use a header node because
we can easily find the first node in the linked list with the First property.
Although it wasn’t used in this example, there is also a Last property
that could be used in the previous While loop to check for the end of the
list:
while (aNode != names.Last) {
Console.WriteLine(aNode.Value);
aNode = aNode.Next;
}
There are two other methods, not shown here, that could prove useful in a linked list implementation: AddFirst and AddLast. These methods can help you implement a linked list without having to provide header and tail nodes in your list.

USING AN ITERATOR CLASS in C#

One problem the LinkedList class has is that you can’t refer to two positions in the linked list at the same time. We can refer to any one position in the list (the current node, the previous node, etc.), but if we want to specify two or more positions, such as if we want to remove a range of nodes from the list, we’ll need some other mechanism. This mechanism is an iterator class.
The iterator class consists of three data fields: a field that stores the linked list, a field that stores the current node, and a field that stores the current node. The constructor method is passed a linked list object, and the method sets the current field to the header node of the list passed into the method. Let’s look at our definition of the class so far:

public class ListIter
{
private Node current;
private Node previous;
LinkedList theList;
public ListIter(LinkedList list)
{
theList = list;
current = theList.getFirst();
previous = null;
}

The first thing we want an Iterator class to do is allow us to move from node to node through the list. The method nextLink does this:

public void NextLink()
{
previous = current;
current = current.link;
}

Notice that in addition to establishing a new current position, the previous node is also set to the node that is current before the method has finished executing. Keeping track of the previous node in addition to the current node makes insertion and removal easier to perform.
The getCurrent method returns the node pointed to by the iterator:
public Node GetCurrent()
{
return current;
}

Two insertion methods are built in the Iterator class: InsertBefore and InsertAfter. InsertBefore inserts a new node before the current node; InsertAfter inserts a new node after the current node. Let’s look at the Insert-Before method first.

The first thing we have to do when inserting a new node before the current object is check to see if we are at the beginning of the list. If we are, then we can’t insert a node before the header node, so we throw an exception. This exception is defined below. Otherwise, we set the new node’s Link field to the Link field of the previous node, set the previous node’s Link field to the new node, and reset the current position to the new node. Here’s the code:

public void InsertBefore(Object theElement)
{
Node newNode = new Node(theElement);
if (current == header)
throw new InsertBeforeHeaderException();
else
{
newNode.Link = previous.Link;
previous.Link = newNode;
current = newNode;
}
}

The InsertBeforeHeader Exception class definition is:

public class InsertBeforeHeaderException
{
public InsertBeforeHeaderException()
{
base("Can't insert before the header node.");
}
}

The InsertAfter method in the Iterator class is much simpler than the method we wrote in the LinkedList class. Since we already know the position of the current node, the method just needs to set the proper links and set the current node to the next node.

public void InsertAfter(Object theElement)
{
Node newnode = new Node(theElement);
newNode.Link = current.Link;
current.Link = newnode;
NextLink();
}

Removing a node from a linked list is extremely easy using an Iterator class. The method simply sets the Link field of the previous node to the node pointed to by the current node’s Link field:

public void Remove()
{
prevous.Link = current.Link;
}

Other methods we need in an Iterator class include methods to reset the iterator to the header node (and the previous node to null) and a method to test if we’re at the end of the list. These methods are shown as follows.

public void Reset()
current = theList.getFirst();
previous = null;
}
public bool AtEnd()
{
return (current.Link == null);
}

Circular Link List Implementation in C#

Circularly Linked List
A circularly linked list is a list where the last node points back to the first node (which may be a header node).

public class Node
{
public Object Element;
public Node Flink;
public Node Blink;
public Node()
{
Element = null;
Flink = null;
Blink = null;
}
public Node(Object theElement)
{
Element = theElement;
Flink = null;
Blink = null;
}
}
public class LinkedList
{
protected Node current;

protected Node header;
private int count;
public LinkedList() {
count = 0;
header = new Node("header");
header.Link = header;
}
public bool IsEmpty() {
return (header.Link == null);
}
public void MakeEmpty() {
header.Link = null;
}
public void PrintList() {
Node current = new Node();
current = header;
while (!(current.Link.Element = "header")) {
Console.WriteLine(current.Link.Element);
current = current.Link;
}
}
private Node FindPrevious(Object n)
{
Node current = header;
while (!(current.Link == null) && current.Link.
Element != n)
current = current.Link;
return current;
}
private Node Find(Object n)
{
Node current = new Node();
current = header.Link;
while (current.Element != n)
current = current.Link;
return current;
}

public void Remove(Object n)
{
Node p = FindPrevious(n);
if (!(p.Link == null)
p.Link = p.Link.Link;
count--;
}
public void Insert(Object n1, n2)
{
Node current = new Node();
Node newnode = new Node(n1);
current = Find(n2);
newnode.Link = current.Link;
current.Link = newnode;
count++;
}
public void InsertFirst(Object n)
{
Node current = new Node(n);
current.Link = header;
header.Link = current;
count++;
}
public Node Move(int n)
{
Node current = header.Link;
Node temp;
for(int i = 0, i <= n; i++)
current = current.Link;
if (current.Element = "header")
current = current.Link;
temp = current;
return temp;
}
}
In the .NET Framework Library, the ArrayList data structure is implemented using a circularly linked list. There are also many problems that can be solved using a circularly linked list.

Doubly Link List Implementation in C#

We first need to modify the Node class to add an extra link to the class. To distinguish between the two links, we’ll call the link to the next node the FLink, and the link to the previous node the BLink. These fields are set to Nothing when a Node is instantiated. Here’s the code:
public class Node
{
public Object Element;
public Node Flink;
public Node Blink;
public Node()
{
Element = null;
Flink = null;
Blink = null;
}
public Node(Object theElement)
{
Element = theElement;
Flink = null;
Blink = null;
}
}

Insertion method


public void Insert(Object newItem, Object after)
{
Node current = new Node();
Node newNode = new Node(newItem);
current = Find(after);
newNode.Flink = current.Link;
newNode.Blink = current;
current.Flink = newNode;
}
Remove method
We first need to find the node in the list; then we set the node’s back link property to point to the node pointed to in the deleted node’s forward link. Then we need to redirect the back link of the link the deleted node points to and point it to the node before the deleted node.

public void Remove(Object n)

{
Node p = Find(n);
if (!(p.Flink == null))

{
p.Blink.Flink = p.Flink;
p.Flink.Blink = p.Blink;
p.Flink = null;
p.Blink = null;
}
}


Printing the elements of a linked list in reverse order

First, we need a method that finds the last node in the list. This is just a matter of following each node’s forward link until we reach a link that points to null. This method, called FindLast, is defined as follows:
private Node FindLast()

{
Node current = new Node();
current = header;
while(!(current.Flink == null))
current = current.Flink;
return current;
}

Once we find the last node in the list, to print the list in reverse order we just follow the backward link until we get to a link that points to null, which indicates we’re at the header node. Here’s the code:
public void PrintReverse()

{
Node current = new Node();
current = FindLast();
while (!(current.Blink == null))

{
Console.WriteLine(current.Element);
current = current.Blink;
}
}

How to write to an event log by using Visual C#

Requirements

The following list describes the recommended hardware, software, network infrastructure, and service packs that you will need:
  • Microsoft Windows 2000 Professional, Windows 2000 Server, Windows 2000 Advanced Server, or Windows NT 4.0 Server
  • Microsoft Visual Studio C#

Write to an event log

Event logging provides a standard, centralized way for your applications to record important software and hardware events. Windows supplies a standard user interface for viewing the logs, the Event Viewer. By using the common language's run-time EventLog component, you can connect to existing event logs easily, on both local and remote computers, and write entries to these logs. You can also read entries from existing logs and create your own custom event logs. In its simplest form, writing to an event log involves only a few steps to create a sample application. To do this, follow these steps:
  1. Open Visual Studio C#.
  2. Create a new Console application in Visual C#. The Console application creates a public class and an empty Main method for you.
  3. Verify that the project references at least the System.dll file.
  4. Use the using directive on the System and System.Diagnostics namespaces so that you do not have to qualify declarations from these namespaces later in your code. You must use these statements before any other declarations.
    using System;
    using System.Diagnostics;
  5. To write to an event log, you must have several pieces of information: Your
    message, the name of the log you to which you want to write (which will be
    created if it does not already exist), and a string that represents the source
    of the event. You can register a particular source with only a single event log;
    if you want to write messages to more than one log, you must define multiple
    sources.
    string sSource;
    string sLog;
    string sEvent;

    sSource = "dotNET Sample App";
    sLog = "Application";
    sEvent = "Sample Event";
  6. Use two static methods of the EventLog class to check whether your source
    exists, and then, if the source does not exist, to create this source that is
    associated with a particular event log. If the log name that you specify does
    not exist, the name is created automatically when you write your first entry to
    the log. By default, if you do not supply a log name to the
    CreateEventSource method, the log file is named "Application Log."
    if (!EventLog.SourceExists(sSource))
    EventLog.CreateEventSource(sSource,sLog);
    1. To write a message to an event log, you can use the EventLog.WriteEntry static method. This method has several different overloaded versions. The following sample code shows the simplest method, which takes a source string and your message, and one of the more complex methods, which supports specifying the event ID and event type:
      EventLog.WriteEntry(sSource,sEvent);
      EventLog.WriteEntry(sSource, sEvent, EventLogEntryType.Warning, 234);
    2. Save your application. Run your application, and then check the Application log in the Event Viewer to see your new events.
    Complete code listing
  7. using System;
    using System.Diagnostics;

    namespace WriteToAnEventLog_csharp
    {
    /// Summary description for Class1.
    class Class1
    {
    static void Main(string[] args)
    {
    string sSource;
    string sLog;
    string sEvent;

    sSource = "dotNET Sample App";
    sLog = "Application";
    sEvent = "Sample Event";

    if (!EventLog.SourceExists(sSource))
    EventLog.CreateEventSource(sSource,sLog);

    EventLog.WriteEntry(sSource,sEvent);
    EventLog.WriteEntry(sSource, sEvent,
    EventLogEntryType.Warning, 234);
    }
    }
    }



Web Farming with the Network Load Balancing Service in Windows Server 2003

Goto this link

http://www.west-wind.com/presentations/loadbalancing/networkloadbalancingwindows2003.asp

How to turn on remote debugging for Visual Studio 2005 in Windows XP with Service Pack 2

The Windows Firewall feature in Windows XP SP2 includes significant enhancements over the earlier the Internet Connection Firewall (ICF) feature. These enhancements help protect the computer from attack by malicious users or by malicious software such as viruses. By default, Windows Firewall is turned on for all network connections including connections to the Internet.

Turn on remote debugging

To turn on remote debugging in Windows XP with SP2, you must configure Windows Firewall as follows:
  • If Windows Firewall is in "shielded" mode, you must perform the appropriate actions so that Windows Firewall is no longer in "shielded" mode.
  • If Windows Firewall is on, you must open some ports. You must also grant some permissions to Microsoft Visual Studio 2005 and to other executable programs that are used in remote debugging.
  • If Windows Firewall is off, you may not have to configure a firewall.
  • Additionally, if the user who runs Microsoft Visual Studio 2005 does not have Administrator user rights on the remote computer, you must configure the DCOM settings on the computer that is running Visual Studio 2005.
To turn on remote debugging, you must have Administrator user rights on the computer that is running Visual Studio 2005. These instructions are only for Internet Protocol version 4 (IPV4) based network settings.

Configure DCOM on the computer that is running Visual Studio 2005

Note After you make changes by using the DCOM Configuration tool (Dcomcnfg.exe), you must restart the computer for the changes to take effect.
  1. At a command prompt, type dcomcnfg, and then press ENTER. Component Services opens.
  2. In Component Services, expand Component Services, expand Computers, and then expand My Computer.
  3. On the toolbar, click Configure My Computer. The My Computer dialog box appears.

    Note If you cannot click Configure My Computer, go to the "Remove and then re-install the MSDTC service" section.
  4. In the My Computer dialog box, click the COM Security tab.
  5. Under Access Permission, click Edit Limits. The Access Permission dialog box appears.
  6. Under Group or user names, click ANONYMOUS LOGON.
  7. Under Permissions for ANONYMOUS LOGON, click to select the Remote Access check box, and then click OK.

Remove and then re-install the MSDTC service

If you cannot click Configure My Computer that is described in step 3 in the "Configure DCOM on the computer that is running Visual Studio 2005" section, follow these steps.

Important This section, method, or task contains steps that tell you how to modify the registry. However, serious problems might occur if you modify the registry incorrectly. Therefore, make sure that you follow these steps carefully. For added protection, back up the registry before you modify it. Then, you can restore the registry if a problem occurs. For more information about how to back up and restore the registry, click the following article number to view the article in the Microsoft Knowledge Base:
322756 (http://support.microsoft.com/kb/322756/ ) How to back up and restore the registry in Windows
  1. Remove the Microsoft Distributed Transaction Service (MSDTC):
    1. Click Start, click Run, type cmd, and then click OK.
    2. At the command prompt, run the following command to stop the MSDTC service:
      Net stop msdtc
    3. At the command prompt, run the following command to remove the MSDTC service:
      Msdtc –uninstall
      The command prompt will return without a message.
  2. In Registry Editor, delete the HKEY_LOCAL_MACHINE\Software\Microsoft\MSDTC subkey.
  3. Re-install and then start the MSDTC service:
    1. At the command prompt, run the following command to re-install the MSDTC service:
      Msdtc –install
    2. At the command prompt, run the following command to start the MSDTC service:
      Net start msdtc
  4. Go to step 4 in the "Configure DCOM on the computer that is running Visual Studio 2005" section.

Configure the computer that is running Visual Studio 2005

Open Windows Firewall

To open Windows Firewall, click Start, click Run, type firewall.cpl, and then click OK.

Open TCP port 135

DCOM remote procedure call (RPC) uses Transfer Control Protocol (TCP) port 135. If the application uses DCOM to communicate with remote computers, port 135 must be open.

To open TCP port 135, follow these steps:
  1. In Windows Firewall, click Add Port on the Exceptions tab. The Add a Port dialog box appears.
  2. In the Name box, type TCP port 135.
  3. In the Port Number box, type 135.
  4. In the Protocol area, click TCP.
  5. Click Change scope to open Change Scope dialog box, click My network (subnet) only, and then click OK. (This step is optional.)
  6. In the Add a Port dialog box, click OK.

Open UDP port 4500

User Datagram Protocol (UDP) port 4500 is used for Internet Protocol security (IPsec). If your domain policy requires that all network communication be completed through IPsec, this port must be open for any network operation. If your domain policy does not require IPsec, go to the "Turn on file and print sharing" section.

To open UDP port 4500, follow these steps:
  1. In Windows Firewall, click Add Port on the Exceptions tab. The Add a Port dialog box appears.
  2. In the Name box, type UDP port 4500.
  3. In the Port Number box, type 4500.
  4. In the Protocol area, click UDP.
  5. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  6. In the Add a Port dialog box, click OK.

Open UDP port 500

UDP port 500 is used for IPsec. If your domain policy requires that all network communication be completed through IPsec, this port must be open for any network operation. If your domain policy does not require IPsec, go to the "Turn on file and print sharing" section.

To open UDP port 500, follow these steps:
  1. In Windows Firewall, click Add Port on the Exceptions tab. The Add a Port dialog box appears.
  2. In the Name box, type UDP port 500.
  3. In the Port Number box, type 500.
  4. In the Protocol area, click UDP.
  5. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  6. In the Add a Port dialog box, click OK.

Turn on file and print sharing

  1. In the Programs and Services area of the Exceptions tab, click File and Print Sharing, and then click Edit. The Edit a Service dialog box appears.
  2. In the Edit a Service dialog box, click to select the following check boxes:
    • TCP 139
    • TCP 445
    • UDP 137
    • UDP 138
  3. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  4. In the Change Scope dialog box, click OK to save your settings.
  5. Click OK to close the Edit a Service dialog box.

Add Devenv.exe to the application exceptions list

To enable applications that cannot run correctly unless the required ports are opened dynamically at runtime, you must add the applications to the application exceptions list.

To add the Visual Studio 2005 Development Environment (Devenv.exe) to the application exceptions list, follow these steps:
  1. In Windows Firewall, click Add Program on the Exceptions tab. The Add a Program dialog box appears.
  2. In the Add a Program dialog box, click Browse. Locate Devenv.exe, and then click OK.

    Note The Devenv.exe file is typically located in the following folder:
    C:\Program Files\Microsoft Visual Studio 8\Common7\IDE
    Microsoft Visual Studio 2005 appears in the Add a Program dialog box.
  3. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  4. In the Add a Program dialog box, click OK.
  5. In Windows Firewall, click OK to save your settings.

Configure the remote computer

All the ports that you have opened on the debugger computer must also be open on the remote computer. To open the TCP 135 port, the UDP 4500 port, and the UDP 500 port, and to turn on file and print sharing, follow the steps in the "" section.

You must also add the Msvsmon.exe file to the application exceptions list.

Add MSVSMon.exe to the application exceptions list

  1. In Windows Firewall, click Add Program on the Exceptions tab. The Add a Program dialog box appears.
  2. In the Add a Program dialog box, click Browse. Locate Msvsmon.exe, and then click OK.

    Note Depending on the computer architecture, the Msvsmon.exe file may be located in any one of the following folders:
    • Drive:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x86
    • Drive:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\x64
    • Drive:\Program Files\Microsoft Visual Studio 8\Common7\IDE\Remote Debugger\ia64
    Visual Studio 2005 Remote Debugger appears in the Add a Program dialog box.
  3. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  4. In the Add a Program dialog box, click OK.
  5. In Windows Firewall, click OK to save your settings.

Enable Web server debugging

HTTP uses TCP port 80. To do Web-based debugging, you must open TCP port 80. This is true for Microsoft ASP.NET debugging, for classic ASP debugging, and for ATL Server debugging.

To open TCP port 80, follow these steps:
  1. In Windows Firewall, click Add Port on the Exceptions tab. The Add a Port dialog box appears.
  2. In the Name box, type TCP port 80.
  3. In the Port Number box, type 80.
  4. In the Protocol area, click TCP.
  5. Click Change scope. The Change Scope dialog box appears. Click My network (subnet) only, and then click OK. (This step is optional.)
  6. In the Add a Port dialog box, click OK.
  7. In Windows Firewall, click OK to save your settings.

Enable script debugging

To debug script code that runs on a remote computer, you must add the process that hosts the script code to the application exceptions list. Typically, in classic ASP debugging, the Dllhost.exe process or the Inetinfo.exe process hosts the script code. However, for a script that runs in Microsoft Internet Explorer, the Iexplore.exe process or in the Explorer.exe process generally hosts the script code.

To add the process that hosts the script code to the application exceptions list, follow these steps:
  1. Click Start, click Run, type firewall.cpl, and then click OK.
  2. In Windows Firewall, click Add Program on the Exceptions tab. The Add a Program dialog box appears.
  3. In the Add a Program dialog box, click Browse. Locate the process that hosts the script code, and then click OK. The application of the process that hosts the script code appears in the Add a Program dialog box.

    For example, if you locate the iexplore.exe process in this step, Internet Explorer appears in the Add a Program dialog box.
  4. In the Scope area, click My network (subnet) only. (This step is optional.)
  5. In the Add a Program dialog box, click OK.
  6. In Windows Firewall, click OK to save your settings.

Run the debugger as a typical user

If you want to run the debugger as a typical user, you must have full user rights to the folder where the executable files are located. Additionally, if you do not have Administrator user rights on the remote computer, you must have access permissions and start permissions to run the debugger as a typical user.

Note A typical user is a user who does not have Administrator user rights.

Note After you make changes by using the DCOM Configuration tool (Dcomcnfg.exe), you must restart the computer for the changes to take effect.

To grant access permissions and start permissions, you must have Administrator user rights. First, obtain Administrator user rights. Then, follow these steps:
  1. At a command prompt, type dcomcnfg, and then press ENTER. Component Services opens.
  2. In Component Services, expand Component Services, expand Computers, and then expand My Computer.
  3. On the toolbar, click Configure My Computer. The My Computer dialog box appears.

    Note If you cannot click Configure My Computer, go to the "Remove and then re-install the MSDTC service" section.
  4. In the My Computer dialog box, click the COM Security tab.
  5. Under Launch and Activate Permissions, click Edit Limits.
  6. If your group or user name does not appear in the Groups or user names list in the Launch Permission dialog box, follow these steps:
    1. In the Launch Permission dialog box, click Add.
    2. In the Select Users or Groups dialog box, enter your user name and your group in the Enter the object names to select box, and then click OK.
  7. In the Launch Permission dialog box, select your user name and your group in the Group or user names box.
  8. In the Allow column under Permissions for User, select Remote Activation, and then click OK.

    Note User is a placeholder for the user name or the group that is selected in the Group or user names box. Repeat steps 7 and 8 for all the users and groups for which you want to grant permissions.

Remove and then re-install the MSDTC service

If you cannot click Configure My Computer that is described in step 3 in the "Run the debugger as a typical user" section, follow these steps.

Important This section, method, or task contains steps that tell you how to modify the registry. However, serious problems might occur if you modify the registry incorrectly. Therefore, make sure that you follow these steps carefully. For added protection, back up the registry before you modify it. Then, you can restore the registry if a problem occurs. For more information about how to back up and restore the registry, click the following article number to view the article in the Microsoft Knowledge Base:
322756 (http://support.microsoft.com/kb/322756/ ) How to back up and restore the registry in Windows
  1. Remove the MSDTC service:
    1. Click Start, click Run, type cmd, and then click OK.
    2. At the command prompt, run the following command to stop the MSDTC service:
      Net stop msdtc
    3. At the command prompt, run the following command to remove the MSDTC service:
      Msdtc –uninstall
      The command prompt will return without a message.
  2. In Registry Editor, delete the HKEY_LOCAL_MACHINE\Software\Microsoft\MSDTC subkey.
  3. Re-install and then start the MSDTC service:
    1. At the command prompt, run the following command to re-install the MSDTC service:
      Msdtc –install
    2. At the command prompt, run the following command to start the MSDTC service:
      Net start msdtc
  4. Go to step 4 in the "Run the debugger as a typical user" section.

MORE INFORMATION

For more information about Windows Firewall, click the following article number to view the article in the Microsoft Knowledge Base:
843090 (http://support.microsoft.com/kb/843090/ ) Description of the Windows Firewall feature in Windows XP Service Pack 2
For more information about Windows XP SP2, visit the following Microsoft Developer Network (MSDN) Web site:
http://msdn2.microsoft.com/en-us/security/aa570371.aspx (http://msdn2.microsoft.com/en-us/security/aa570371.aspx)
For more information about remote debugging, click the following article number to view the article in the Microsoft Knowledge Base:
833977 (http://support.microsoft.com/kb/833977/ ) How to turn on remote debugging in Windows XP with Service Pack 2

How To Create an ASP.NET HTTP Handler by Using Visual C# .NET

Implement the Handler

  1. Open Microsoft Visual Studio .NET. In Visual C# .NET, create a new Class Library project named MyHandler.
  2. Set a reference to the System.Web.dll assembly.
  3. Add the following directive to the class.
using System.Web;

4. Rename the class SyncHandler.cs, and then change the class definition to reflect this.
5.Implement the IHttpHandler interface. Your class definition should appear as follows:

public class SyncHandler : IHttpHandler

6. Implement the IsReusable property and the ProcessRequest method of the IHttpHandler interface. Because this is a synchronous handler, return False for the IsReusable property so that the handler is not pooled.

public bool IsReusable
{
get
{
return false;
}
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello from custom handler.");
}

7. Compile the project.

Deploy the Handler


  1. Create a new directory named Handler under the C:\Inetpub\Wwwroot directory.
  2. Create a subdirectory named Bin in the newly created Handler directory. The resultant path is C:\Inetpub\Wwwroot\Handler\Bin.
  3. Copy MyHandler.dll from your project's Bin\Debug directory to the C:\Inetpub\Wwwroot\Handler\Bin directory.
  4. Follow these steps to mark the new Handler directory as a Web application:
    1. Open Internet Services Manager.
    2. Right-click the Handler directory, and then click Properties.
    3. On the Directory tab, click Create.
  5. Follow these steps to create an application mapping for the handler. For this handler, create a mapping to the Aspnet_isapi.dll file for the *.sync extension. Whenever a .sync file is requested, the request is routed to ASP.NET, and ASP.NET executes the code in the handler.
    1. Right-click on the Handler Web application, and then click Properties.
    2. On the Directory tab, click Configuration.
    3. Click Add to add a new mapping.
    4. In the Executable text box, type the following path: Microsoft Windows 2000:
      C:\WINNT\Microsoft.NET\Framework\\Aspnet_isapi.dll
      Microsoft Windows XP:
      C:\WINDOWS\Microsoft.NET\Framework\\Aspnet_isapi.dll
    5. In the Extension text box, type .sync.
    6. Make sure that the Check that file exists check box is cleared, and then click OK to close the Add/Edit Application Extension Mapping dialog box.
    7. Click OK to close the Application Configuration and the Handler Properties dialog boxes.
  6. Close Internet Services Manager.

Configure the System

  1. In the C:\Inetpub\Wwwroot\Handler directory, create a new file named Web.config.
  2. Add the following code to Web.config:


In the verb="*" attribute, we instruct the handler to process a request that uses any verb (for example, POST, HEAD, GET, and so on). If you want this handler to process only the POST request, change this to verb="POST".

In the path="*.sync" attribute, we instruct the handler to process any incoming requests for files with the .sync extension.

In the type="MyHandler.SyncHandler, MyHandler" attribute, we instruct the handler that processes the request to implement in the MyHandler.SyncHandler namespace, and this class resides in the MyHandler assembly.

Test the Module

To test a handler, a page does not need to exist in the file system. For example, request the Default.sync file in the Handler Web application (http:///Handler/Default.sync). You should receive the following results:

Hello from custom handler.

How to use the Debug and the Trace classes

This article describes how to use the Debug and the Trace classes. These classes are available in the Microsoft .NET Framework. You can use these classes to provide information about the performance of an application either during application development, or after deployment to production. These classes are only one part of the instrumentation features that are available in the .NET Framework.

Requirements

The following list outlines the recommended hardware, software, network infrastructure, and service packs that you need:
  • Microsoft Windows 2000 or Microsoft Windows XP or Microsoft Windows Server 2003
  • Microsoft Visual C#
This article also assumes that you are familiar with program debugging.

Description Of Technique


The steps in the Create a Sample with the Debug Class section demonstrate how to create a console application that uses the Debug class to provide information about the program execution.

When the program is run, you can use methods of the Debug class to produce messages that help you to monitor the program execution sequence, to detect malfunctions, or to provide performance measurement information. By default, the messages that the Debug class produces appear in the Output window of the Visual Studio Integrated Development Environment (IDE).

The sample code uses the WriteLine method to produce a message that is followed by a line terminator. When you use this method to produce a message, each message appears on a separate line in the Output window.

When you use the Assert method of the Debug class, the Output window displays a message only if a specified condition evaluates to false. The message also appears in a modal dialog box to the user. The dialog box includes the message, the project name, and the Debug.Assert statement number. The dialog box also includes the following three command buttons:
  • Abort: The application stops running.
  • Retry: The application enters debug mode.
  • Ignore: The application proceeds.
The user must click one of these buttons before the application can continue.

You can also direct output from the Debug class to destinations other than the Output window. The Debug class has a collection named Listeners that includes Listener objects.

Each Listener object monitors Debug output and directs the output to a specified target.

Each Listener in the Listener collection receives any output that the Debug class generates. Use the TextWriterTraceListener class to define Listener objects. You can specify the target for a TextWriterTraceListener class through its constructor.

Some possible output targets include the following:
  • The Console window by using the System.Console.Out property.
  • A text (.txt) file by using the System.IO.File.CreateText("FileName.txt") statement.
After you create a TextWriterTraceListener object, you must add the object to the Debug.Listeners collection to receive Debug output

Create a Sample with the Debug Class

  1. Start Visual Studio or Visual C# Express Edition.
  2. Create a new Visual C# Console Application project named conInfo. Class1 is created in Visual Studio .NET. Program.cs is created in Visual Studio 2005.
  3. Add the following namespace at top in Class1 or Program.cs.
using System.Diagnostics;

4.To initialize variables to contain information about a product, add the following declaration statements to Main method:
string sProdName = "Widget";
int iUnitQty = 100;
double dUnitCost = 1.03;

5.Specify the message that the class produces as the first input parameter of the
WriteLine method. Press the CTRL+ALT+O key combination to make sure that
the Output window is visible.

Debug.WriteLine("Debug Information-Product Starting ");

6.For readability, use the Indent method to indent subsequent messages in
the Output window:
Debug.Indent();

7.To display the content of selected variables, use the WriteLine method as
follows:
Debug.WriteLine("The product name is " + sProdName);
Debug.WriteLine("The available units on hand are" + iUnitQty.ToString());

8.You can also use the WriteLine method to display the namespace and the
class name for an existent object. For example, the following code displays the
System.Xml.XmlDocument namespace in the Output window:
System.Xml.XmlDocument oxml = new System.Xml.XmlDocument();

9.To organize the output, you can include a category as an optional, second input
parameter of the WriteLine method. If you specify a category, the format
of the Output window message is "category: message." For example, the first line
of the following code displays "Field: The product name is Widget" in the Output
window:
Debug.WriteLine("The product name is " + sProdName,"Field");
Debug.WriteLine("The units on hand are" + iUnitQty,"Field");
Debug.WriteLine("The per unit cost is" + dUnitCost.ToString(),"Field");
Debug.WriteLine("Total Cost is " + (iUnitQty * dUnitCost),"Calc");

Debug.WriteLine(oxml);

Debug.WriteLine("The per unit cost is " + dUnitCost.ToString());
10.The Output window can display messages only if a designated condition evaluates
to true by using the WriteLineIf method of the Debug class. The
condition to be evaluated is the first input parameter of the WriteLineIf
method. The second parameter of WriteLineIf is the message that appears
only if the condition in the first parameter evaluates to true.
Debug.WriteLineIf(iUnitQty > 50, "This message WILL appear");

11.
  1. Use the Assert method of the Debug class so that the Output window displays the message only if a specified condition evaluates to false:
    Debug.Assert(dUnitCost > 1, "Message will NOT appear");
    Debug.Assert(dUnitCost < 1, "Message will appear since dUnitcost < 1 is false");
Debug.WriteLineIf(iUnitQty < 50, "This message will NOT appear");
12. Create the TextWriterTraceListener objects for the Console window (tr1) and for a text file named Output.txt (tr2), and then add each object to the Debug Listeners collection:
TextWriterTraceListener tr1 = new TextWriterTraceListener(System.Console.Out);
Debug.Listeners.Add(tr1);

TextWriterTraceListener tr2 = new TextWriterTraceListener(System.IO.File.CreateText("Output.txt"));

13.For readability, use the Unindent method to remove the indentation for
subsequent messages that the Debug class generates. When you use the
Indent and the Unindent methods together, the reader can
distinguish the output as group.
Debug.Unindent();
Debug.WriteLine("Debug Information-Product Ending");

Debug.Listeners.Add(tr2);
14. To make sure that each Listener object receives all its output, call the Flush method for the Debug class buffers:
Debug.Flush();

Using the Trace Class

You can also use the Trace class to produce messages that monitor the execution of an application. The Trace and Debug classes share most of the same methods to produce output, including the following:
  • WriteLine
  • WriteLineIf
  • Indent
  • Unindent
  • Assert
  • Flush
You can use the Trace and the Debug classes separately or together in the same application. In a Debug Solution Configuration project, both Trace and Debug output are active. The project generates output from both of these classes to all Listener objects. However, a Release Solution Configuration project only generates output from a Trace class. The Release Solution Configuration project ignores any Debug class method invocations.
Trace.WriteLine("Trace Information-Product Starting ");
Trace.Indent();

Trace.WriteLine("The product name is "+sProdName);
Trace.WriteLine("The product name is"+sProdName,"Field" );
Trace.WriteLineIf(iUnitQty > 50, "This message WILL appear");
Trace.Assert(dUnitCost > 1, "Message will NOT appear");

Trace.Unindent();
Trace.WriteLine("Trace Information-Product Ending");

Trace.Flush();

Console.ReadLine();


Verify That It Works

  1. Make sure that Debug is the current solution configuration.
  2. If the Solution Explorer window is not visible, press the CTRL+ALT+L key combination to display this window.
  3. Right-click conInfo, and then click Properties.
  4. In the left pane of the conInfo property page, under the Configuration folder, make sure that the arrow points to Debugging.

    Note In Visual C# 2005 and in Visual C# 2005 Express Edition, click Debug in the conInfo page.
  5. Above the Configuration folder, in the Configuration drop-down list box, click Active (Debug) or Debug, and then click OK. In Visual C# 2005 and in Visual C# 2005 Express Edition, click Active (Debug) or Debug in the Configuration drop-down list box in the Debug page, and then click Save on the File menu.
  6. Press CTRL+ALT+O to display the Output window.
  7. Press the F5 key to run the code. When the Assertion Failed dialog box appears, click Ignore.
  8. In the Console window, press ENTER. The program should finish, and the Output window should display the output that resembles the following
        Debug Information-Product Starting
    The product name is Widget
    The available units on hand are100
    The per unit cost is 1.03
    System.Xml.XmlDocument
    Field: The product name is Widget
    Field: The units on hand are100
    Field: The per unit cost is1.03
    Calc: Total Cost is 103
    This message WILL appear
    ---- DEBUG ASSERTION FAILED ----
    ---- Assert Short Message ----
    Message will appear since dUnitcost < 1 is false
    ---- Assert Long Message ----


    at Class1.Main(String[] args) <%Path%>\class1.cs(34)

    The product name is Widget
    The available units on hand are100
    The per unit cost is 1.03
    Debug Information-Product Ending
    Trace Information-Product Starting
    The product name is Widget
    Field: The product name isWidget
    This message WILL appear
    Trace Information-Product Ending

  9. The Console window and the Output.txt file should display the following output:
    The product name is Widget
    The available units on hand are 100
    The per unit cost is 1.03
    Debug Information-Product Ending
    Trace Information-Product Starting
    The product name is Widget
    Field: The product name is Widget
    This message WILL appear
    Trace Information-Product Ending
Note The Output.txt file is located in the same directory as the conInfo executable (conInfo.exe). Typically, this is the \bin folder where the project source is stored. By default, this is C:\Documents and Settings\User login\My Documents\Visual Studio Projects\conInfo\bin. In Visual C# 2005 and in Visual C# 2005 Express Edition, the Output.txt file is located in the following folder:
C:\Documents and Settings\User login\My Documents\Visual Studio 2005\Projects\conInfo\conInfo\bin\Debug

Complete Code Listing
using System;
using System.Diagnostics;

class Class1
{
[STAThread]
static void Main(string[] args)
{
string sProdName = "Widget";
int iUnitQty = 100;
double dUnitCost = 1.03;
Debug.WriteLine("Debug Information-Product Starting ");
Debug.Indent();
Debug.WriteLine("The product name is "+sProdName);
Debug.WriteLine("The available units on hand are"+iUnitQty.ToString()); Debug.WriteLine("The per unit cost is "+ dUnitCost.ToString());

System.Xml.XmlDocument oxml = new System.Xml.XmlDocument();
Debug.WriteLine(oxml);
Debug.WriteLine("The product name is "+sProdName,"Field");
Debug.WriteLine("The units on hand are"+iUnitQty,"Field");
Debug.WriteLine("The per unit cost is"+dUnitCost.ToString(),"Field");
Debug.WriteLine("Total Cost is "+(iUnitQty * dUnitCost),"Calc");
Debug.WriteLineIf(iUnitQty > 50, "This message WILL appear");
Debug.WriteLineIf(iUnitQty < 50, "This message will NOT appear");
Debug.Assert(dUnitCost > 1, "Message will NOT appear");
Debug.Assert(dUnitCost < 1, "Message will appear since dUnitcost < 1 is false");


TextWriterTraceListener tr1 = new TextWriterTraceListener(System.Console.Out); Debug.Listeners.Add(tr1);
TextWriterTraceListener tr2 = new TextWriterTraceListener(System.IO.File.CreateText("Output.txt")); Debug.Listeners.Add(tr2);
Debug.WriteLine("The product name is "+sProdName);
Debug.WriteLine("The available units on hand are"+iUnitQty);
Debug.WriteLine("The per unit cost is "+dUnitCost);
Debug.Unindent();
Debug.WriteLine("Debug Information-Product Ending");
Debug.Flush();
Trace.WriteLine("Trace Information-Product Starting ");
Trace.Indent();
Trace.WriteLine("The product name is "+sProdName);

Trace.WriteLine("The product name is"+sProdName,"Field" );
Trace.WriteLineIf(iUnitQty > 50, "This message WILL appear");
Trace.Assert(dUnitCost > 1, "Message will NOT appear");
Trace.Unindent();
Trace.WriteLine("Trace Information-Product Ending");
Trace.Flush(); Console.ReadLine();
}
}