DotNetSlackers: ASP.NET News for lazy Developers

Thursday, January 20, 2011

Fetching ASP.NET authenticated page with HTTPWebRequest


For some purposes we needed to fetch data from an authenticated page of ASP.NET. When I try to browse that page it go to the login page. In the login page there have user name and password field and want to login to the page clicking on submit button.
In this case when user type user name and password and submit then in server side there has code on button click handler to check user name and password. So for authenticating to the page using HTTPWebRequest we need to know how ASP.NET send event to submit click handler. ASP.NET page has two hidden variables understand from server-side which button is clicked.
 Collapse
<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
And also when button is clicked then a javascript function is called which set the name of the button in __EVENTTARGET and command argument in_EVENTARGUMENT
 Collapse
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
So if we set the __EVENTTARGET value as button name then in server side of ASP.NET page life cycle it it raise postback event and call the Button event with the argument. You can see the button argument to understand which event is set to __EVENTARGUMENT hidden variable. The page which we want to authenticate have nothing as command argument. so it go as empty string. So when we request data we have to send username, password, and also __EVENTARGET as button name and __EVENTARGUMENT as empty string. Then it will call the Button event with user name and password.
Our used HTTP web request class looks like this
 Collapse
public WebPostRequest(string url, CookieContainer  cookieContainer) 
       { 
            theRequest = (HttpWebRequest)WebRequest.Create(url); 
            theRequest.CookieContainer = cookieContainer; 
           theRequest.Method = "POST"; 
           theQueryData = new ArrayList(); 
       }
public void Add(string key, string value) 
      { 
          theQueryData.Add(String.Format("{0}={1}", key, HttpUtility.UrlEncode(value))); 
      }
Here you can see it create a request and set the cookie container with give cookie. As we are authenticating the page so authenticated session is stored in cookie. So we need to assign the cookie container were cookies will be stored so that sending the same cookie we can request other page which we want to actually request.
So for first time when we want to login to the page then the we create the request like
 Collapse
CookieContainer cookieContainer = new CookieContainer(); 
          WebPostRequest myPost = new WebPostRequest(
               http://samplehost/sample/LoginAdmin.aspx, cookieContainer); 
               myPost.Add("LoginAdmin$UserName", "username"); 
          myPost.Add("LoginAdmin$Password", "password"); 
          myPost.Add("__EVENTTARGET", "LoginAdmin$SubmitButton"); 
          myPost.Add("__EVENTARGUMENT", "");
myPost.GetResponse();
You can see here a cookie container is added and trying to authenticate by calling LoginAdmin.aspx page adding query data . Now when we try to GetResponse with post request then it will fill the cookie information in the cookie container. So next time we will send this cookie container for request and the site will treat me as authenticated user. So the response code here
 Collapse
public string GetResponse() 
       {// Set the encoding type 
           theRequest.ContentType = "application/x-www-form-urlencoded"; 
           // Build a string containing all the parameters 
           string Parameters = String.Join("&", 
               (String[])theQueryData.ToArray(typeof(string))); 
           theRequest.ContentLength = Parameters.Length; 
           // We write the parameters into the request 
           StreamWriter sw = new StreamWriter(theRequest.GetRequestStream()); 
           sw.Write(Parameters); 
           sw.Close(); 
           // Execute the query 
           theResponse = (HttpWebResponse)theRequest.GetResponse(); 
           StreamReader sr = new StreamReader(theResponse.GetResponseStream()); 
           HttpStatusCode code = theResponse.StatusCode; 
           return sr.ReadToEnd(); 
       }
from the response string you can understand that you have authenticated to the page. But other target page was not the LoginAdmin.aspx. We called this page for authentication and also get authenticated cookie in our cookie container. So now we can send request again with then same cookie container to get the output of desired page.
 Collapse
myPost = new WebPostRequest("http://samplehost/sample/Targetpage.aspx", cookieContainer); 
            myPost.Add("ctl00$cphPage$txtDate", "04/11/2010"); 
                    myPost.Add("__EVENTTARGET", "ctl00_cphPage_btnSend"); 
            myPost.Add("__EVENTARGUMENT", ""); 
            string FinalRespose = myPost.GetResponse();
So far I have discussed here how we can request a authenticated authenticated ASP.NET authenticated page using HTTPWebRequest to fetch data from code. After that we can do anything with the retrieved output.

Linq DLinq XLinq PLinq All at one place


Introduction

Yes, I know there are many articles on the internet on this topic and many are on CodeProject itself. However, the thing that I found missing was a single place where a newbie could get all the related information about LINQ. Later, one can drill down on the topics of his/her interest.
Through this article, I am trying to share information about this one of the salient feature of the .NET. I am looking forward to enable a beginner to write the code in the LINQ way if he is not doing that already. Also I hope to make one start thinking of the solutions in the same direction. Of course, this doesn't provide all the details about the topic. This article gives you the cursors and general understanding about LINQ.

What is LINQ

LINQ stands for the Language Integrated Query. It relies on the language improvements (C# 3.0 and VB 9.0 onwards).
This is what Microsoft says about LINQ:
"LINQ is a set of extensions to the .NET Framework that encompass language-integrated query, set, and transform operations. It extends C# and Visual Basic with native language syntax for queries and provides class libraries to take advantage of these capabilities." In simple terms, LINQ is something you can use for writing the queries that fits your needs and operates against various data stores including remote one, e.g. SQL, etc.

Why Use LINQ

In general programming scenarios, we queries various kinds of data stores, e.g., object collections, database and XML, etc. For querying these data stores, we have got to use different mechanisms that are best suited for them. For example, we use foreach loop for querying the collection, write SQL queries for database and Xpath queries for querying the XML data.
So the point here is that we need to know individual terminology for doing similar things when we got to work with different data stores. To make my point more clear, consider the scenario where we need to find the customers from the specific region (say India).
Query using object:
 Collapse
foreach(Customer c in customers) 
if (c.Region == “India") … 
Query from database table:
 Collapse
SELECT * FROM Customers WHERE Region='India'
Query XML using xPath:
 Collapse
//Customers/Customer[@Region=‘India'] 
What LINQ tries to do here is that it provides you the UNIFORM APPROACH for querying and manipulating the different data sources. Therefore, for querying the data, you do not need to learn XPath, SQL and Object iterations. All you need to know is how to query using LINQ. Once you start using it in your day to day applications, you will realize the real power of it.
Moreover, LINQ comes with some perks which are as follows:
  • Bridges the gap between OO and Relational data
  • Integrated directly in the language
  • Type checking and compile time safety
  • Intellisense
  • Debugger support

Sample LINQ Query and Its Dissection

A simple Linq query looks similar to the one as follows:
 Collapse
var contacts = from c in customers
where c.Region == "India"
select new {c.Name, c.Phone}
This small code fragment contains few of the language features supported by the 3.0 framework onwards which are identified in the following illustration. Both the queries are the same, it's just the notation that is changed.
sampleq.PNG
Following are the terms that have popped up from a simple LINQ query.

1) Standard Query Expression

These are the language syntax which are there to assist you writing the LINQ queries that are understood by the .NET Framework.
qexp.png

2) Local Variable Type Inference

This allows you to use var instead of explicitly specifying the type. Linq queries usually return IEnumerable<T>. In some cases, it's convenient to use the anonymous types. Point to notice here is that the variable deduces the variable type from the initializer. Scope of the var is always local.
 Collapse
int i = 666;
string s = "Goodbye";
double d = 3.14;
int[] numbers = new int[] {1, 2, 3};
Dictionary<int,Order> orders = new Dictionary<int,Order>();
could be replaced by the following:
 Collapse
var i = 666;
var s = "Goodbye";
var d = 3.14;
var numbers = new int[] {1, 2, 3};
var orders = new Dictionary<int,Order>();

3) Object Initializers

This allows you to create an instance of an object in just one line by providing the values for the properties:
 Collapse
public class Point
{
    private int x, y;
    public int X { get { return x; } set { x = value; } }
    public int Y { get { return y; } set { y = value; } }
}
The following line creates an instance of Point object with the parameters provided.
 Collapse
Point a = new Point { X = 0, Y = 1 };

4) Anonymous Types

Usually we create the types with some name. This feature allows you to create the type that does not have any name.
 Collapse
var i1 = new {CustomerId = 123, Name = “santosh” };
 var i2 = new {123, “santosh”};

5) Lambda Expressions

Definition:
A lambda expression is an anonymous function that can contain expressions and statements.
Let's see how the Lambda expression get evolved. Let's say we want to get the customers which have the City as "London". To do this, one can use one of our old traditional friends named delegate.
  1. Getting the customers using delegate:
     Collapse
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(CityEqualsLondon);
    private bool CityEqualsLondon(Customer c){
                return c.City == "London";
    }
  2. Let's say you do not want to create a function, e.g., CityEqualsLondon() that does the filtering stuff, you can use the anonymous method instead. It's just inline and you get the desired result.
     Collapse
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(delegate(Customer c) { return c.City == "London"; });
  3. Why not have something that does the required thing without writing some common syntax that the compiler can anyway find out by looking at the code. The solution that came out was lambda expressions.
     Collapse
    List<Customer> customers = GetCustomers();
    var local = customers.FindAll(c => c.City == "London");
So the c => c.city == "London" is a lambda expression that contains all the required information to give to compiler so the compiler knows what to do. "=>" is read as "goes to".
Following are two terms that you should better be knowing when using the LINQ or Lambda expressions.

Predicate

 Collapse
(p) => p.Gender == “F” 
//All persons, p, such that person’s Gender is “F" 

Projection

 Collapse
(p) => p.Gender ? “F” : “Female” 
//“Each person p becomes string “Female” if Gender is “F 

6) Extension Methods

This feature lets you add methods to the existing types without creating the derived type.
Following are the items that explains the Extension Methods.
  • Extends Existing Types
  • Adds Methods Without Derivation
  • Accesses Public Members of Extended Types
  • Must be:
    • public and static
    • Housed within a static class
  • Use this keyword before parameter of extended type
Following is the code to create a sample extension method named ToMsgbox that extends the strings array in such way that the strings gets concatenated with new lines and gets displayed in a message box. It's not a very good example, however, fair enough to explain the basic idea behind this.
 Collapse
public static class MyExtns
    {
        public static void ToMsgbox(this string[] strings)
        {
            MessageBox.Show(string.Join(Environment.NewLine, strings));
        }
    }

LINQ Architecture

The following figure explains the basic architecture of the LINQ. You write the LINQ queries in the language of your choice that operates on the LINQ enabled data sources. There are basically three main logical branches for the LINQ. LINQ to Object, LINQ to Database and Entities (known as DLINQ) and LINQ to XML (known as XLINQ).
arch.PNG

LINQ to Objects

LINQ to Objects sample:
 Collapse
string[] testStrings = { "Santosh", "Vikram", "Mohit", "Chirag", 
"Amit", "Vikas", "Vipul", "Andy" };
            
            var temp = from str in testStrings
                       let words = str.ToLower().ToCharArray()
                       where str.Length > 4 && words[0] == 'v'
                       orderby str.Length descending
                       select str;
            temp.ToArray().ToMsg();

LINQ to SQL/Entities (DLINQ)

When you query the database and entities using the LINQ queries, it's referred as DLINQ. Visual Studio provides you the option to do the OR mapping in the integrated designer. You can check out more details from one of the ScottGu's blog here.
Here are some points to note about DLINQ:
  • ORM Designer Maps Relational Structures to Classes
  • Pushes query processing from in-memory to a remote data store
  • Automatically generates parameterized SQL
  • Fully supports stored procedures
  • Tables have CRUD definitions (Default = use runtime)
    • Can instead point to stored procedures
  • Built on the concept of Deferred Loading
The following figure explains how the LINQ to SQL works:
linq2sql.PNG
Following is the sample code snippet to explain the deferred execution. You see, the actual query is triggered to the database when DataBind() is executed. So the execution of actual query keeps on getting deferred up to the time till there is no other option. Most query operators don’t execute when declared.
 Collapse
//Define query 
var query = from c in ctx.Customers 
where c.Region == “India“ 
select c; 

//Execute query 
gridViewCustomers.DataSource = query; 
gridViewCustomers.DataBind(); //Execute here 
In addition to this,
  • Query gets executed when enumerated
  • Bound to a DataBound control
  • Execute within ForEach loop Iteration
  • Transformed to collection ToList()ToArray(), etc.

Deferred Execution

  • Benefits:
    • Fetches data only when needed, not all at once
    • Minimize network traffic, memory consumption and load on database
    • Great for when can but fetch objects as user requests them
  • Drawbacks:
    • Chatty Interface: Each request for child record invokes explicit database query

Immediate Execution

  • Benefits:
    • Singe query returns all data
    • Great if you know that you will need all data returned
  • Drawbacks:
    • Large amount of data returned, whether used or not
Here is what an ORM designer look like:
nwind.PNG
Here come a few sample DLINQ queries:

Select products

 Collapse
var products = from p in _NWindDataContext.Products
where p.CategoryID == 1
select new { p.ProductName, p.CategoryID };
 Collapse
dataGridView1.DataSource = products; 

Insert a product

 Collapse
Product product = new Product();
product.CategoryID = 1;
product.ProductName = "testProduct";
_NWindDataContext.Products.InsertOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();

Update Product

 Collapse
var product = (from p in _NWindDataContext.Products
where p.CategoryID == 1 && p.ProductName.Contains("testProduct")
select p).First();
product.ProductName = "santosh";
_NWindDataContext.SubmitChanges();
RefreshData();

Delete Product

 Collapse
var product = (from p in _NWindDataContext.Products
     where p.CategoryID == 1 && p.ProductName.Contains("santosh")
     select p).First();
_NWindDataContext.Products.DeleteOnSubmit(product);
_NWindDataContext.SubmitChanges();
RefreshData();

LINQ to XML (XLINQ)

Is simple words, when you write your LINQ queries that operates on the XML, they are referred as XLINQ queries.
 Collapse
XDocument xdoc = XDocument.Load("auths.xml");
 var auths = from a in xdoc.Root.Descendants("author")
                        where a.Element("au_fname").Value.Contains("a")
                        orderby a.Element("au_fname").Value.Length descending
                        select a.Element("au_fname").Value;

Expression Tree

Expression trees represent code in a tree-like data structure, where each node is an expression. You can create an expression tree of lambda expressions that are quite useful when you query using LINQ and there are many conditional filters (where clauses) you want to apply on the query.
 Collapse
public delegate bool Predicate<T>(T item);

Expression<Predicate<Customer>> test = c => c.Region== "India";

ParameterExpression c = Expression.Parameter(typeof(Customer), "c");
Expression expr = Expression.Equal(
        Expression.Property(c, typeof(Customer).GetProperty("Region")),
        Expression.Constant("India")
    );

Expression<Predicate<Customer>> test =
    Expression.Lambda<Predicate<Customer>>(expr, c);

Parallel LINQ - (PLINQ)

Framework 4.0 introduces parallel processing. LINQ also got its share by letting the LINQ query run in parallel. How the query gets executed in parallel is a complicated process. I do not want to burden you with more details about this. Please find the link at the end of this article, in case you want to have more details.
plinq.PNG
From the programming perspective, if you want to know how to write a LINQ query that takes advantage of this feature, please refer to the following code snippet and notice the AsParallel():
 Collapse
//LINQ to Object Query
int[] output = arr
    .Select(x => Test(x))
    .ToArray();

//PLinq Query
int[] output = arr.AsParallel()
    .Select(x => Test(x))
    .ToArray(); 

Saturday, January 8, 2011

Silver Draw - A Silverlight Based Collaboration White Board with Drawing and Chat


Introduction

Silverlight + WCF Polling Duplex services = Real Awesomeness. In this article, I'll show how to create a real-time white board that can sync information between various participants, using Silverlight + Polling Duplex. Silverlight + WCF Polling Duplex services enables you to write Silverlight apps that can share information almost real time between users, over HTTP.
Users can draw together in the white board, and may chat with each other. Here is a quick screenshot of the client running in two different browsers. And if you are more interested, have a look at this video of the app, in my blog.
The app has a login panel, a couple of drawing tools (pencil, pen, and a brush), a drawing canvas, a chat area, a color picker to choose color for line and fill, and a notification area to view activities from users in the session.

Internals made simple

Needless to say, the objective of the article is to explain how Polling Duplex can be implemented along with Silverlight and WCF. The application has two parts:
  1. Server - A WCF service with a polling duplex end point for clients to register, to publish information, and to receive notifications.
  2. Client - A Silverlight client that consumes the end point to register the user, to publish the user's drawing, and to receive and plot the drawing data from other users.
The server side web service interface (IDuplexDrawService) has these important methods:
  • Register - A client may call this to register itself to a conversation.
  • Draw - A client may call this to request the server to pump the drawing information to other clients in the conversation
  • NotifyClients - From the Draw method, we'll invoke NotifyClients to iterate the registered clients, to publish the data.
Also, as we are using Polling Duplex, we need a callback interface too - so that the server can 'call back' or notify the clients. (In Polling Duplex, you may need to note that what is happening is not actually a direct callback from server to client. The client should poll the server periodically over HTTP to fetch any possible information that the server is willing to pass to the client, to simulate a callback.) Anyway, our 'call back' service interface (IDuplexDrawCallback) has a Notify method, for the server to notify the rest of the clients when some client calls the Draw method.
So, in short - o party may publish some information using 'Draw', and others can subscribe to the published information. This is a simple implementation of the Publisher/Subscriber pattern.
When you load the client the first time, you'll be asked for a username to join the session. From there, the logic goes something like:
In client-side:
  • The client will try to connect to the service end point.
  • If success, the client will register itself with the client, by calling the Register method, and by passing the username.
  • The client will hook up a few event handlers in the proxy. Mainly, the NotifyCompleted event. NotifyCompleted will be fired each time you receive a callback from the server.
In server-side:
  • Inside the Register method, the server will grab the client's session ID and callback channel from the current operation context, and add it to a list.
  • Whenever a client submits information by calling 'Draw', the server will pump this information by iterating each registered client, and by calling their 'Notify' method.

Server implementation

Let us have a quick look at how the server side implementation is done. First of all, you need to add a reference toSystem.ServiceModel.PollingDuplexService.dll in your Silverlight SDK's Server folder. For me, this path is: C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Server\.

Service and callback interfaces

Now, let us create a simple interface for our service - IDuplexDrawService.
 Collapse
[ServiceContract (CallbackContract = typeof(IDuplexDrawCallback))]
public interface IDuplexDrawService
{
    [OperationContract(IsOneWay = true)]
    void Register(string name);
    [OperationContract(IsOneWay = true)]
    void Draw(string data);
}
You may note that we are specifying the CallbackContract attribute as the type of the callback interface. The callback interface looks like:
 Collapse
[ServiceContract]
public interface IDuplexDrawCallback
{
    [OperationContract(IsOneWay = true, 
          AsyncPattern = true, Action = DrawData.DrawAction)]
    IAsyncResult BeginNotify(Message message, 
           AsyncCallback callback, object state);
    void EndNotify(IAsyncResult result);
}
Nothing special there, other than that we are specifying our Notify method as two parts, to facilitate an asynchronous call. Later, we'll create a message from our custom object to pass it as the first parameter of BeginNotify. We should specify a message contract as well.
 Collapse
[MessageContract]
public class DrawData
{
    public const string DrawAction = 
               "http://amazedsaint.net/SilverPaint/draw";

    [MessageBodyMember]
    public string Content { get; set; }

    [MessageBodyMember]
    public string From { get; set; }
}
Alright, now we have the pieces ready, so let us create the concrete service. At this point, you might need to have a look at the DuplexDraw service source code for a side reference. Firstly, note that we are specifying a couple of attributes for the service.
 Collapse
[ServiceBehavior
        (ConcurrencyMode = ConcurrencyMode.Multiple, 
        InstanceContextMode = InstanceContextMode.Single)]
public class DuplexDrawService : IDuplexDrawService
{
   //Code here
}
InstanceContextMode is set to Single, which means only one instance context is used for handling all incoming client calls. Also,ConcurrencyMode is set to Multiple so that the service instance will be multi-threaded (we'll end up doing some explicit locking).
We may not go through all the methods in the service, but essentially the idea is as follows. As mentioned earlier, after creating a connection, the client should call the Register method. Inside the Register method in the server, we'll grab the callback channel and add it to a dictionary, with Session ID as the key.
 Collapse
string sessionId = OperationContext.Current.Channel.SessionId;
var callback = 
    OperationContext.Current.GetCallbackChannel   
                            <IDuplexDrawCallback>();

lock (syncRoot)
{
    clients[sessionId] = callback;
    userNames[sessionId] = name;
}

Draw and NotifyClients

The Draw and NotifyClients methods in the service are self explanatory. When some client calls the Draw method, we'll iterate the dictionary we have, to publish the data to all subscribed clients. Here is the essence of what is happening in the Draw and NotifyClients methods.
Inside the Draw method, we grab the incoming data to notify the connected clients.
 Collapse
/// A client will call this, to publish the drawing data
public void Draw(string data)
{
    lock (this.syncRoot)
    {
        string sessionId = 
          OperationContext.Current.Channel.SessionId;
        if (userNames.ContainsKey(sessionId))
            NotifyClients("@draw:" + data, userNames
           [sessionId],sessionId);
    }
}
Inside the NotifyClients method in the service, we actually pump the data to various clients. The NotifyClients method is as below. We create a message buffer from the DrawData.
 Collapse
//In the actual code, this is a global static variable
static TypedMessageConverter messageConverter = 
TypedMessageConverter.Create(
  typeof(DrawData),
  DrawData.DrawAction,
  "http://schemas.datacontract.org/2004/07/SilverPaintService");


/// Send the notification to all clients
public void NotifyClients(string data,string from,string sessionId)
{
    MessageBuffer notificationMessageBuffer = 
        messageConverter.ToMessage
        (new DrawData 
      { Content = data, From = from }).CreateBufferedCopy
        (65536);
    foreach (var client in clients.Values)
    {
        try
        {
           client.BeginNotify
               (notificationMessageBuffer.CreateMessage(), 
               onNotifyCompleted, client);
        }
        catch
        {}
    }
}
If you are wondering what is there in the data variable, it is a JSON serialized string that contains the drawing board information. We'll see more about that when we walk over the client side.

Endpoint configuration

And finally, a quick word on configuring the end points. Have a look at the system.ServiceModel section in the server web.config. Especially, have a look at the extensions section, where we are adding a new binding extension, named pollingDuplex. Then, we need to create a custom binding type (customBinding) and specify that as the binding of our endpoint.
 Collapse
<extensions>
    <bindingElementExtensions>
      <add name="pollingDuplex" 
        type="System.ServiceModel.Configuration.PollingDuplexElement, 
              System.ServiceModel.PollingDuplex"/>
    </bindingElementExtensions>
    <bindingExtensions>
      <add name="pollingDuplex" 
        type="System.ServiceModel.Configuration.PollingDuplexElement, 
              System.ServiceModel.PollingDuplex"/>
    </bindingExtensions>
  </extensions>
  <behaviors>
    <serviceBehaviors>
      <behavior
         name="SilverdrawServiceBehaviour">
        <serviceMetadata httpGetEnabled="true"/>
        <serviceDebug 
            includeExceptionDetailInFaults="true"/>
        <serviceThrottling maxConcurrentSessions
                 ="2147483647"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <bindings>
    <customBinding>
      <binding name="SilverdrawServiceBinding">
        <binaryMessageEncoding />
        <pollingDuplex 
          inactivityTimeout="02:00:00" 
          serverPollTimeout="00:05:00"
          maxPendingMessagesPerSession="2147483647" 
          maxPendingSessions="2147483647" />
        <httpTransport />
      </binding>
    </customBinding>
  </bindings>
  <services>
    <service behaviorConfiguration="SilverdrawServiceBehaviour" 
         name="Silverdraw.Server.DuplexDrawService">
      <endpoint address="" 
         binding="customBinding"
         bindingConfiguration="SilverdrawServiceBinding"
         contract="Silverdraw.Server.IDuplexDrawService"/>
      <endpoint address="mex" 
         binding="mexHttpBinding" 
         contract="IMetadataExchange"/>
    </service>
  </services>
Now, let us examine what is there in the client side.

Silverlight client

You need to add a reference to System.ServiceModel.PollingDuplexService.dll in your Silverlight SDK's Client folder. For me, this path is: C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\.

Connecting to the server

In the client side, most of the work is done inside the DuplexClientHelper class. First of all, we need to add a service reference, and create a proxy for the service we created.
DuplexClientHelper is a thin wrapper on top of the proxy generated out of the service.
When the Silverlight control is loaded, we'll show a quick login panel, and from the button click there, we'll invoke the Initialize method inDuplexClientHelper.cs, via the InitServiceConnection method in Page.xaml.cs.
There, we need to create a new client instance, and wire up the events to receive notifications.
 Collapse
public void Initialize(string endPointAddress)
{
    this.client = new Proxy.DuplexDrawServiceClient(
    new PollingDuplexHttpBinding(),
    new EndpointAddress(endPointAddress));

    this.client.NotifyReceived += new 
        EventHandler<proxy.notifyreceivedeventargs>
                (client_NotifyReceived);
}
As you might have guessed, NotifyReceived is invoked whenever the server pumps a message to the client. When we receive the notification, we may fetch the DrawingData from the request. Here is a stripped down version of the event handler:
 Collapse
/// Callback to get the notification
void client_NotifyReceived
   (object sender, Proxy.NotifyReceivedEventArgs e)
{
    var data = e.request.GetBody<drawdata>();
}

Drawing logic

Most of the drawing logic is in DrawingArea.cs and Page.xaml.cs in our Silverlight client. Most of the code is self explanatory - we use the traditional way of drawing - setting a boolean flag to true in mouse down, to draw the elements in mouse move.
Shapes are drawn to the canvas with respect to the selected tool (brush, pen etc.), by invoking a couple of methods in DrawingArea.cs, from the mouse down events in Page.xaml.cs.
Remember - when we add shapes to the canvas, we need to publish this information to other clients, right? For this, we have a temporary list of shapes in the DrawingArea class - where we keep the shapes added to the local canvas. When the number of shapes in our temporary list reaches a specific count, we serialize the shapes to a JSON string, and pass it to the server to publish - using the Draw method described earlier.
You may also note that we are converting the shapes to a serializable object model (see ScreenObject.cs) before doing the actual serialization. The methods for converting the shapes back and forth from the ScreenObject type resides in the CanvasHelper.cs file.
And how do we convert this to JSON? We have a couple of extension methods in the JsonSerializerHelper.cs file. Also, I have a blog post here on these extension methods and how JSON works.