Powered By Blogger
Showing posts with label .Net Refrences. Show all posts
Showing posts with label .Net Refrences. Show all posts

Monday, 14 March 2011

Seven Important Facts About ASP.NET

The .NET Framework is divided into an almost painstaking collection of functional parts, with a
staggering total of more than 7,000
core programming ingredients). Before you can program any type of .NET application, you need a
basic understanding of those parts—and an understanding of why things are organized the way
they are.
The massive collection of functionality that the .NET Framework provides is organized in a way
that traditional Windows programmers will see as a happy improvement. Each one of the thousands
of classes in the .NET Framework is grouped into a logical, hierarchical container called a
Different namespaces provide different features. Taken together, the .NET namespaces offer
functionality for nearly every aspect of distributed development from message queuing to security.
This massive toolkit is called the
Interestingly, the way you use the .NET Framework classes in ASP.NET is the same as the way
you use them in any other type of .NET application (including a stand-alone Windows application,
a Windows service, a command-line utility, and so on). In other words, .NET gives the same tools to
web developers that it gives to rich client developers.
If you’ve programmed extensively with ASP.NET 1.
is available in ASP.NET 2.0. The difference is that ASP.NET 2.0 adds even more classes to the mix,
many in entirely new namespaces for features such as configuration, health monitoring, and
personalization.

Fact 2: ASP.NET Is Compiled,Not Interpreted
One of the major reasons for performance degradation in ASP scripts is that all ASP web-page code
uses interpreted scripting languages. This means that when your application is executed, a scripting
host on the server machine needs to interpret your code and translate it to lower-level machine
code, line by line. This process is notoriously slow.
ASP.NET applications are always compiled—in fact, it’s impossible to execute C# or VB .NET
code without it being compiled first.
ASP.NET applications actually go through two stages of compilation. In the first stage, the C#
code you write is compiled into an intermediate language called Microsoft Intermediate Language
(MSIL) code, or just IL. This first step is the fundamental reason that .NET can be languageinterdependent.
Essentially, all .NET languages (including C#, Visual Basic, and many more) are
compiled into virtually identical IL code. This first compilation step may happen automatically
when the page is first requested, or you can perform it in advance (a process known as
precompiling
The second level of compilation happens just before the page is actually executed. At this
point, the IL code is compiled into low-level native machine code. This stage is known as
). The compiled file with IL code is an assembly.just-intime
(JIT) compilation, and it takes place in the same way for all .NET applications (including
Windows applications, for example). Figure 1-1 shows this two-step compilation process.
.NET compilation is decoupled into two steps in order to offer developers the most convenience
and the best portability. Before a compiler can create low-level machine code, it needs to
know what type of operating system and hardware platform the application will run on (for example,
32-bit or 64-bit Windows). By having two compile stages, you can create a compiled assembly
with .NET code but still distribute this to more than one platform.

Fact 3: ASP.NET Is Multilanguage
Though you’ll probably opt to use one language over another when you develop an application,
that choice won’t determine what you can accomplish with your web applications. That’s because
no matter what language you use, the code is compiled into IL.
IL is a stepping-stone for every managed application. (A
that’s written for .NET and executes inside the managed environment of the CLR.) In a sense,
IL is
To understand IL, it helps to consider a simple example. Take a look at this function, written
in C#:
managed application is any applicationthe language of .NET, and it’s the only language that the CLR recognizes.
namespace HelloWorld
{
public class TestClass
{
private static void Main(string[] args)
{
Console.WriteLine("Hello World");
}
}
}
This code shows the most basic application that’s possible in .NET—a simple command-line
utility that displays a single, predictable message on the console window.
CHAPTER 1
INTRODUCING ASP.NET 9
Now look at it from a different perspective. Here’s the IL code for the same class:
.method public static void Main() cil managed
{
.entrypoint
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor() =
( 01 00 00 00 )
// Code size 14 (0xe)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: nop
IL_000d: ret
} // end of method Module1::Main
It’s easy enough to look at the IL for any compiled .NET application. You simply need to run the
IL Disassembler, which is installed with Visual Studio and the .NET SDK (software development kit).
Look for the file ildasm.exe in a directory like c:\Program Files\Visual Studio 2005\SDK\v2.0\Bin.
Once you’ve loaded the program, use the File
was created with .NET.
If you’re patient and a little logical, you can deconstruct the IL code fairly easily and figure
out what’s happening. The fact that IL is so easy to disassemble can raise privacy and code control
issues, but these issues usually aren’t of any concern to ASP.NET developers. That’s because all
ASP.NET code is stored and executed on the server. Because the client never receives the compiled
code file, the client has no opportunity to decompile it. If it
that scrambles code to try to make it more difficult to understand. (For example, an obfuscator
might rename all variables to have generic, meaningless names such as f__a__234.) Visual Studio
includes a scaled-down version of one popular obfuscator, called Dotfuscator.
The following code shows the same console application in Visual Basic code:
Open command, and select any DLL or EXE thatis a concern, consider using an obfuscator
Namespace HelloWorld
Public Class TestClass
Private Shared Sub Main(Ars() As String)
Console.WriteLine("Hello World")
End Sub
End Class
End Namespace
If you compile this application and look at the IL code, you’ll find that every line is identical to
the IL code generated from the C# version. Although different compilers can sometimes introduce
their own optimizations, as a general rule of thumb no .NET language outperforms any other .NET
language, because they all share the same common infrastructure. This infrastructure is formalized
in the CLS (Common Language Specification), which is described in the “The Common Language
Specification” sidebar.
It’s important to note that IL was recently adopted as an ANSI (American National Standards
Institute) standard. This adoption could quite possibly spur the adoption of other common language
frameworks. The Mono project at

Fact 4: ASP.NET Runs Inside the Common Language Runtime
Perhaps the most important aspect of ASP.NET to remember is that it runs inside the runtime
engine of the CLR. The whole of the .NET Framework—that is, all namespaces, applications, and
classes—are referred to as
the scope of this chapter, some of the benefits are as follows:
managed code. Though a full-blown investigation of the CLR is beyond
Automatic memory management and garbage collection
an object, the CLR allocates space on the
never need to clear this memory manually. As soon as your reference to an object goes out of
scope (or your application ends), the object becomes available for garbage collection. The
garbage collector runs periodically inside the CLR, automatically reclaiming unused memory
for inaccessible objects. This model saves you from the low-level complexities of C++ memory
handling and from the quirkiness of COM reference counting.
: Every time your application instantiatesmanaged heap for that object. However, you
Type safety
indicates details such as the available classes, their members, their data types, and so on. As a
result, your compiled code assemblies are completely self-sufficient. Other people can use
them without requiring any other support files, and the compiler can verify that every call is
valid at runtime. This extra layer of safety completely obliterates low-level errors such as the
infamous buffer overflow.
: When you compile an application, .NET adds information to your assembly that
Extensible metadata
metadata that .NET stores in a compiled assembly.
you to provide additional information to the runtime or other services. For example, this metadata
might tell a debugger how to trace your code, or it might tell Visual Studio how to display a
custom control at design time. You could also use metadata to enable other runtime services
(such as web methods or COM+ services).
: The information about classes and members is only one of the types ofMetadata describes your code and allows
Structured error handling
VBScript code, you’ll most likely be familiar with the limited resources these languages offer
for error handling. With structured exception handling, you can organize your error-handling
code logically and concisely. You can create separate blocks to deal with different types of
errors. You can also nest exception handlers multiple layers deep.
: If you’ve ever written any moderately useful Visual Basic or
Multithreading
you can call methods, read files, or communicate with web services asynchronously, without

Fact 5: ASP.NET Is Object-Oriented
ASP provides a relatively feeble object model. It provides a small set of objects; these objects are
really just a thin layer over the raw details of HTTP and HTML. On the other hand, ASP.NET is truly
object-oriented. Not only does your code have full access to all objects in the .NET Framework, but
you can also exploit all the conventions of an OOP (object-oriented programming) environment.
For example, you can create reusable classes, standardize code with interfaces, and bundle useful
functionality in a distributable, compiled component.
One of the best examples of object-oriented thinking in ASP.NET is found in
Server-based controls are the epitome of encapsulation. Developers can manipulate control
objects programmatically using code to customize their appearance, provide data to display, and
even react to events. The low-level HTML details are hidden away behind the scenes. Instead of
forcing the developer to write raw HTML manually, the control objects render themselves to HTML
when the page is finished rendering. In this way, ASP.NET offers server controls as a way to abstract
server-based controls.
12
CHAPTER 1 INTRODUCING ASP.NET
Here’s a quick example with a standard HTML text box:
<input type="text" id="myText" runat="server" />
With the addition of the runat="server" attribute, this static piece of HTML becomes a fully
functional server-side control that you can manipulate in your code. You can now work with events
that it generates, set attributes, and bind it to a data source.
For example, you can set the text of this box when the page first loads using the following code:
void Page_Load(object sender, EventArgs e)
{
myText.Value = "Hello World!";
}
Technically, this code sets the Value property of an HtmlInputText object. The end result is that
a string of text appears in a text box on the HTML page that’s rendered and sent to the client.

Fact 6: ASP.NET Is Multidevice and Multibrowser
One of the greatest challenges web developers face is the wide variety of browsers they need to
support. Different browsers, versions, and configurations differ in their support of HTML. Web
developers need to choose whether they should render their content according to HTML 3.2,
HTML 4.0, or something else entirely—such as XHTML 1.0 or even WML (Wireless Markup Language)
for mobile devices. This problem, fueled by the various browser companies, has plagued
developers since the World Wide Web Consortium proposed the first version of HTML. Life gets
even more complicated if you want to use an HTML extension such as JavaScript to create a more
dynamic page or provide validation.
ASP.NET addresses this problem in a remarkably intelligent way. Although you can retrieve
information about the client browser and its capabilities in an ASP.NET page, ASP.NET actually
encourages developers to ignore these considerations and use a rich suite of web server controls.
These server controls render their HTML adaptively by taking the client’s capabilities into account.
One example is ASP.NET’s validation controls, which use JavaScript and DHTML (Dynamic HTML)
to enhance their behavior if the client supports it. This allows the validation controls to show
dynamic error messages without the user needing to send the page back to the server for more processing.
These features are optional, but they demonstrate how intelligent controls can make the
most of cutting-edge browsers without shutting out other clients. Best of all, you don’t need any
extra coding work to support both types of client.

Fact 7: ASP.NET Is Easy to Deploy and Configure
One of the biggest headaches a web developer faces during a development cycle is deploying a
completed application to a production server. Not only do the web-page files, databases, and components
need to be transferred, but you also need to register components and re-create a slew of
configuration settings. ASP.NET simplifies this process considerably.
Every installation of the .NET Framework provides the same core classes. As a result, deploying
an ASP.NET application is relatively simple. In most cases, you simply need to copy all the files to a
virtual directory on a production server (using an FTP program or even a command-line command
like XCOPY). As long as the host machine has the .NET Framework, there are no time-consuming
registration steps.
Distributing the components your application uses is just as easy. All you need to do is copy the
component assemblies when you deploy your web application. Because all the information about
your component is stored directly in the assembly file metadata, there’s no need to launch a registration
program or modify the Windows registry. As long as you place these components in the
correct place (the Bin subdirectory of the web application directory), the ASP.NET engine automatically
detects them and makes them available to your web-page code. Try that with a traditional
COM component!

 

If you’re new to ASP.NET (or you just want to review a few fundamentals), you’ll be interested in the
following sections. They introduce seven touchstones of .NET development.


Fact 1: ASP.NET Is Integrated with the .NET Framework

ReDim Statement (from msdn)

ReDim
Used at procedure level to reallocate storage space for an array variable.

ReDim [ Preserve ] name(boundlist)

Parts
Preserve
Optional. Keyword used to preserve the data in the existing array when you change the size of only the last dimension.
name
Required. Name of the variable. Must be a valid Visual Basic identifier. You can redimension as many variables as you like in the same statement, specifying the name and boundlist parts for each one. Multiple variables are separated by commas.
boundlist
Required. List of non-negative integers representing the upper bounds of the dimensions of the redefined array. Multiple upper bounds are separated by commas. The number of dimensions in boundlist must match the original rank of the array.
Each value in boundlist specifies the upper bound of a dimension, not the length. The lower bound is always zero, so the subscript for each dimension can vary from zero through the upper bound.

It is possible to use -1 to declare the upper bound of an array dimension. This signifies that the array is empty but not Nothing, a distinction required by certain common language runtime functions. However, Visual Basic code cannot successfully access such an array. If you attempt to do so, an IndexOutOfRangeException error occurs during execution.

Remarks
  • The ReDim statement can appear only at procedure level. This means you can redefine arrays inside a procedure but not at class or module level.
  • The ReDim statement is used to change the size of one or more dimensions of an array that has already been formally declared. ReDim cannot change the rank (the number of dimensions) of the array.
  • The ReDim statement cannot change the data type of an array variable or provide new initialization values for the array elements.
  • ReDim releases the existing array and creates a new array with the same rank. The elements of the new array are initialized to the default value for their data type unless you specify Preserve.

If you include the Preserve keyword, Visual Basic copies the elements from the existing array to the new array. When you use Preserve, you can resize only the last dimension of the array, and for every other dimension you must specify the same size it already has in the existing array.

For example, if your array has only one dimension, you can resize that dimension and still preserve the contents of the array, because it is the last and only dimension. However, if your array has two or more dimensions, you can change the size of only the last dimension if you use Preserve.

The following example increases the size of the last dimension of a dynamic array without losing any existing data in the array, and then decreases the size with partial data loss:

Dim IntArray(10, 10, 10) As Integer
' ...
ReDim Preserve IntArray(10, 10, 20)
' ...
ReDim Preserve IntArray(10, 10, 15)

The first ReDim creates a new array, copying all the elements from the existing array. It also adds 10 more columns to the end of every row in every layer. The elements in these new columns are initialized to the default value of the element type of the array.

The second ReDim creates another new array, copying all the elements that fit. However, five columns are lost from the end of every row in every layer. This is not a problem if you have finished using these columns. Reducing the size of a large array can free up memory that you no longer need.

You can use ReDim on a property that holds an array of values.

Example
This example uses the ReDim statement to allocate and reallocate storage space for array variables.

Dim I, MyArray() As Integer ' Declare variable and array variable.
ReDim MyArray(5) ' Allocate 6 elements.
For I = 0 To UBound(MyArray)
MyArray(I) = I ' Initialize array.
Next I
The next statement resizes the array without saving the contents of the elements.


ReDim MyArray(10) ' Resize to 11 elements.
For I = 0 To UBound(MyArray)
MyArray(I) = I ' Initialize array.
Next I

The following statement resizes the array but saves the contents of the elements.

ReDim Preserve MyArray(15) ' Resize to 16 elements.

Wednesday, 9 March 2011

Remove duplicates Items in array.

IN VB.NET

Public Function RemoveDuplicateItems(ByVal itemList As String()) As String()
Dim tempArray As ArrayList = New ArrayList
For i As Integer = 0 To itemlist.Count - 1
If Not tempArray.Contains(itemList.GetValue(i)) Then
tempArray.Add(itemList.GetValue(i))
End If
Next
Return tempArray.ToArray(GetType(String))
End Function

Dim List As String() = {"Nokia", "Samsung", "LG", "Motorola", "Nokia", "Samsung"}
RemoveDuplicateItems(List)

Monday, 7 March 2011

Find out which webserver is behind a url

Find out which webserver is behind a url

import System.Net

using response=WebRequest.Create("http://www.orkut.com").GetResponse():
print(response.Headers["Server"])

If you are behind a non-transparent firewall/proxy you may need to set the proxy credentials so the example would change a bit:

import System.Net

request = WebRequest.Create("http://www.orkut.com")
proxy = WebProxy.GetDefaultProxy()
proxy.Credentials = NetworkCredential("username", "password")
request.Proxy = proxy

using response=request.GetResponse():
print(response.Headers["Server"])

Using the Web Service Callbacks in the .NET Application

Introduction

The Web Services can be used as a simple connectable service for a Web Page consumer in the request-respond manner. Usually these services are using a local resources such as application servers, databases, file servers, etc. In this case, the Web Service looks like an Internet wrapper to the local application. This article describes using the Web Service as a url address driven connectable component in the application model. I will show you how can be invoked virtually any Web Service in the loosely coupled design model included their callbacks. Based on these features, the application-distributed model can be virtualized and driven by metadata known as the Application Knowledge Base. In the details you will find implementation of the Virtual Web Service Proxy and callback mechanism between the Web Services using the C# language.

The Concept and Design

The client invoking the Web Service using the proxy (wrapper) class derived from the base class for ASP.NET Web Service - HttpWebClientProtocol. This class does all underlying work of the WebMethod mapping into the SOAP message method invocations (the SOAP is the default protocol). The proxy class can be generated by the wsdl.exe utility and incorporated into the project. Note that the proxy class is created for each Web Service separately. The client needs to know about the Web Services in advance of the compiling project. This approach is suitable for the tightly coupled application models. What about the situation, where client doesn't know which Web Service is going to be invoked? Well, for this "logical" connectivity the proxy class has to be created on the fly, based on the wsdl description of the requested Web Service. The concept of the Logical connectivity has the loosely coupled design pattern driven by the Knowledge Base (KB), which is a database of the application metadata such as wsdl source, url, state, etc.
The following picture shows the position of the Web Services in the .NET Application model:

The concept of the Web Service allows to "plug&play" service to the "Software Bus", which is represented by the Internet. Note that the Web Services run behind the firewall. After that point they can use others .NET Technologies such as .NET Remoting, .NET Services (COM+ Services), etc.
The Web Services connectivity to the Software Bus is bidirectional - listening or sending messages. On the other hand, the Application Services or Web clients are passive and they have only capability to consume the Web Service. Typically, the Application Server using less one Web Service to dispatch incoming messages from the Software Bus. This architecture model is opened also for "legacy" applications, where the Web Service play role of the gateway to map the Web Methods to the application specific service.
The other feature of the Software Bus advantage is data exchange and their abstract definitions. This is achieved using the XML technology, which it allows to use different platforms or implementations for the Web Services.
Using the Web Services in your application model has some rules similar to components which running in the COM+/MTS environment, the following are major:
  • WebMethod is stateless; the Web Service has responsibility to keep a state between the calls. Some good idea is to use a "travelling" message in the business model to keep a job state (similar to transaction stream)
  • WebMethod runs synchronously, the client should be use asynchronously invoking method, which it will yield its process to perform another calls.
  • WebMethod is a root of the transaction stream; there is no option for "transaction support". If the WebMethod has been declared as a transactional (required or new), then this place is a root of the new transaction. Note that transaction over Internet is not supported by Web services in the current version of the .NET Framework. The transaction has to be divided into more local transactions, see a concept and model of the MSMQ transactions.
  • Object reference cannot be used in arguments or return value; it means that only object state can be exchange between consumer and Web Service. Practically, the sender serializing an object - its public state into the XML format and receiver as opposite side making its de-serialization.
The above architecture model organized around the Software Bus allows building incrementally any business model in the true distributed hierarchy. The picture shows only typically browser-based client, but practically it can be any devices which have an access to the Internet included bridges and gateways (for instance; wireless internet gateway).
Now, let's look at the details of the connectable Web Service mechanism.

The Web Service Callback

The following picture shows an example of the Web Service callbacks:
The Web Service "A" has a dedicated WebMethod to handle callbacks from the Web Service "B". Each Web Service has own global State to keep the state between the calls. The access to the Web Method is via the Virtual Proxy class and it is divided into two phases:
  • Client calling a proxy class in either the sync or async manner using the BeginInvoke/EndInvoke design pattern.
  • Proxy class calling the Web Method over Internet in the sync manner.
The Web Service "A" needs to pass an additional two callback's arguments to the Web Service "B"; the first one is a callback service and the other one is its state. This mechanism is similar to the BeginInvoke/EndInvoke design pattern. The callback Web Method signature should have two arguments; senderId and EventArgs class. The return value is a bool, which is a processing flag (continue or abort). The calls are in the loosely coupled design pattern based on the metadata (url address of the wsdl). How is it working? Well, the browser-based client sending a request to the Web Service "A" to make some work. The Web Service "A" needs a help from the Web Service "B" which it will take some certain time. During this time the Web Service "A" will receive callbacks from the Web Service "B". The client can refresh a page about the current status or send a new request to abort its original request.
The following code snippet shows how simple is this process implemented using the WebServiceAccessor class.
Collapse
[WebMethod]
  public string DoSomeWorkA(int count, string ticket)
  {
    // ...

    // call the WebService B

    WebServiceAccessor wsa = new WebServiceAccessor(targetWsdlUrl);
    object esObj = wsa.CreateInstance("ServiceB");
    object retval = wsa.Invoke(esObj, "BeginDoSomeWorkB", count, 
            myWsdlUrl, state, null, null);
    // ...        

  }
  [WebMethod]
  public bool CallbackServiceA(string sender, string xmlEventArg)
  {
    WebServiceEventArg ea = (WebServiceEventArg)xmlEventArg;
    //...

  }

Implementation

The key of the above design is to have a virtually access to any Web Service on the fly based on its wsdl description. To achieve this requirement the Web Service client proxy has to be virtualized to allow invoking the Web Service from any place. The WebServiceAccessor is a class, which can handle this task. I implemented it as a separate assembly and it is easy to add into the .NET projects. Also, there is a WebServiceEventArgs class required for the callback method.

Virtual Web Service Proxy

The idea of the Virtual Service Proxy is to generate metadata of the specified Web Service on the fly (in memory) for the Reflection process. As an entry parameter can be used a direct wsdl description or indirect information where this description can be obtained it. The indirect implantation is done for sources such as the File System and URL address. All process is divided into three pieces and it shown in the following picture:
The source code of the Web Service is stored in the class for test purpose only. Its image is exactly the same like the file generated by wsdl.exe utility. For this part of the implementation I have been inspired by article [1] and [2], thanks. Having the source code of the proxy, then it is easy to compile it and generate its assembly. Once we have a proxy assembly we can use the Reflection magic to initiate a proxy class and then invoking its method members.
The following code snippet is shown its implementation:
Collapse
// Virtual Web Service Accessor

  public class WebServiceAccessor
  {
    private Assembly _ass = null;               
        // assembly of the web service proxy

    private string _protocolName = "Soap";      
        // communication protocol

    private string _srcWSProxy = string.Empty;  
       // source text (.cs) 

    //

    public Assembly Assembly { get{ return _ass; } }
    public string ProtocolName 
      { get{ return _protocolName; } set {_protocolName = value; } }
    public string SrcWSProxy { get{ return _srcWSProxy; } }

    public WebServiceAccessor()
    {
    }
    public WebServiceAccessor(string wsdlSourceName)
    {
      AssemblyFromWsdl(GetWsdl(wsdlSourceName));
    }
    // Get the wsdl text from specified source

    public string WsdlFromUrl(string url)
    {
      WebRequest req = WebRequest.Create(url);
      WebResponse result = req.GetResponse();
      Stream ReceiveStream = result.GetResponseStream();
      Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
      StreamReader sr = new StreamReader( ReceiveStream, encode );
      string strWsdl = sr.ReadToEnd();
      return strWsdl;
    }
    public string GetWsdl(string source) 
    {
      if(source.StartsWith("<?xml version") == true)
      {
        return source;                // this can be a wsdl string

      }
      else
      if(source.StartsWith("http://") == true)
      {
      return WsdlFromUrl(source);     // this is a url address

      }
                    
      return WsdlFromFile(source);    // try to get from the file system

    }
    public string WsdlFromFile(string fileFullPathName)
    {
      FileInfo fi = new FileInfo(fileFullPathName);
      if(fi.Extension == "wsdl")
      {
        FileStream fs = new FileStream(fileFullPathName, FileMode.Open, 
            FileAccess.Read);
        StreamReader sr = new StreamReader(fs);
        char[] buffer = new char[(int)fs.Length];
        sr.ReadBlock(buffer, 0, (int)fs.Length);
        return new string(buffer);
      }
                
      throw new Exception("This is no a wsdl file");
    }
    // make assembly for specified wsdl text

    public Assembly AssemblyFromWsdl(string strWsdl)
    {
      // Xml text reader

      StringReader  wsdlStringReader = new StringReader(strWsdl);
      XmlTextReader tr = new XmlTextReader(wsdlStringReader);
      ServiceDescription sd = ServiceDescription.Read(tr);
      tr.Close();

      // WSDL service description importer 

      CodeNamespace cns = new CodeNamespace("RKiss.WebServiceAccessor");
      ServiceDescriptionImporter sdi = new ServiceDescriptionImporter();
      sdi.AddServiceDescription(sd, null, null);
      sdi.ProtocolName = _protocolName;
      sdi.Import(cns, null);

      // source code generation

      CSharpCodeProvider cscp = new CSharpCodeProvider();
      ICodeGenerator icg = cscp.CreateGenerator();
      StringBuilder srcStringBuilder = new StringBuilder();
      StringWriter sw = new StringWriter(srcStringBuilder);
      icg.GenerateCodeFromNamespace(cns, sw, null);
      _srcWSProxy = srcStringBuilder.ToString();
      sw.Close();

      // assembly compilation.

      CompilerParameters cp = new CompilerParameters();
      cp.ReferencedAssemblies.Add("System.dll");
      cp.ReferencedAssemblies.Add("System.Xml.dll");
      cp.ReferencedAssemblies.Add("System.Web.Services.dll");
      cp.GenerateExecutable = false;
      cp.GenerateInMemory = true; 
      cp.IncludeDebugInformation = false; 
      ICodeCompiler icc = cscp.CreateCompiler();
      CompilerResults cr = icc.CompileAssemblyFromSource(cp, _srcWSProxy);
      if(cr.Errors.Count > 0)
        throw new Exception(string.Format("Build failed: {0} errors", 
            cr.Errors.Count)); 

      return _ass = cr.CompiledAssembly;
    }
    // Create instance of the web service proxy

    public object CreateInstance(string objTypeName) 
    {
      Type t = _ass.GetType("RKiss.WebServiceAccessor" + "." + objTypeName);
      return Activator.CreateInstance(t);
    }
    // invoke method on the obj

    public object Invoke(object obj, string methodName, params object[] args)
    {
      MethodInfo mi = obj.GetType().GetMethod(methodName);
      return mi.Invoke(obj, args);
    }
  }

WebServiceEventArgs class

The WebServiceEventArgs class is used to pass a callback state. This is an example for the concept validation. The class state is depended from the application, but less one field should be declared - _state. This field is a callback cookie. The other thing, there are two methods to serialize/de-serialize class in the XML fashion format. I used implicit and explicit operators to make an easy casting to/from string.
Collapse
// The callback state class

  [Serializable]
  public class WebServiceEventArgs : EventArgs
  {
    private string    _name;
    private string    _state;
    private int     _param;
    //

    public string name 
    {
      get{ return _name; }
      set{ _name = value; }
    }
    public string state 
    {
      get{ return _state; }
      set{ _state = value; }
    }
    public int param 
    {
      get{ return _param; }
      set{ _param = value; }
    }
    public static implicit operator string(WebServiceEventArgs obj) 
    {
      StringBuilder xmlStringBuilder = new StringBuilder();
      XmlTextWriter tw = new XmlTextWriter(new StringWriter(
          xmlStringBuilder));
      XmlSerializer serializer = new XmlSerializer(
         typeof(WebServiceEventArgs));
      serializer.Serialize(tw, obj);
      tw.Close();
      return xmlStringBuilder.ToString();    
    }
    public static explicit operator WebServiceEventArgs(string xmlobj) 
    {
      XmlSerializer serializer = new XmlSerializer(
          typeof(WebServiceEventArgs));
      XmlTextReader tr = new XmlTextReader(new StringReader(xmlobj));
      WebServiceEventArgs ea = 
           serializer.Deserialize(tr) as WebServiceEventArgs;
      tr.Close();
      return ea;
    }
  }

Test

The concept and design of the Web Service Callbacks can be tested with the following three web subprojects located in the http://localhost virtual directory. The test schema is shown in the above section - The Web Service Callback. All projects has incorporated the Trace points to see the process flow on the DebugView screen (http://www.sysinternals.com/).
Here are their implementations:

1. Web Service A

Collapse
namespace WebServiceA
{
  public class ServiceA : System.Web.Services.WebService
  {
    public ServiceA()
    {
      //CODEGEN: This call is required by the ASP.NET Web Services Designer

      InitializeComponent();
      Trace.WriteLine(string.Format("[{0}]ServiceA.ctor", GetHashCode()));
    }

    #region Component Designer generated code
    private void InitializeComponent()
    {
    }
    #endregion

    protected override void Dispose( bool disposing )
    {
      Trace.WriteLine(string.Format("[{0}]ServiceA.Dispose", GetHashCode()));
    }

    [WebMethod]
    public string DoSomeWorkA(int count, string ticket)
    {
      Trace.WriteLine(string.Format("[{0}]ServiceA.DoSomeWorkA start...", 
        GetHashCode()));
      int startTC = Environment.TickCount;

      // current state

      Global.state[ticket] = "The job has been started";
      string state = ticket;
    
      // location of the source/target web services - 

      // (hard coded for test purpose only!) 

      string myWsdlUrl = "http://localhost/WebServiceA/ServiceA.asmx?wsdl";
      string targetWsdlUrl = "http://localhost/WebServiceB/ServiceB.asmx?wsdl";
            
      // call the WebService B

      WebServiceAccessor wsa = new WebServiceAccessor(targetWsdlUrl);
      object esObj = wsa.CreateInstance("ServiceB");
      object retval = wsa.Invoke(esObj, "BeginDoSomeWorkB", count, 
         myWsdlUrl, state, null, null);

      // Wait for the call to complete

      WebClientAsyncResult ar = retval as WebClientAsyncResult;
      ar.AsyncWaitHandle.WaitOne();

      // retrieve a result

      object result = wsa.Invoke(esObj, "EndDoSomeWorkB", ar);
      int durationTC = Environment.TickCount - startTC;
      Trace.WriteLine(string.Format("[{0}]ServiceA.DoSomeWorkA done in {1}ms", 
         GetHashCode(), durationTC));

      //

      Global.state.Remove(ticket);
      return result.ToString();
    }

    [WebMethod]
    public bool CallbackServiceA(string sender, string xmlEventArg)
    {
      WebServiceEventArgs ea = (WebServiceEventArgs)xmlEventArg;
      string msg = string.Format(
             "[{0}]ServiceA.CallbackServiceA({1}, [{2},{3},{4}])", 
             GetHashCode(), sender, ea.name, ea.state, ea.param);

      if(Global.state.ContainsKey(ea.state))
      {
        Global.state[ea.state] = string.Format("{0}, [{1},{2},{3}]", 
             sender, ea.name, ea.state, ea.param);
        Trace.WriteLine(msg);
        return true;
      }
            
      return false;
    }

    [WebMethod]
    public string AbortWorkA(string ticket)
    {
      Trace.WriteLine(string.Format("[{0}]ServiceA.AbortWorkA", 
           GetHashCode()));

      if(Global.state.ContainsKey(ticket))
      {
        Global.state.Remove(ticket);
        return string.Format("#{0} aborted.", ticket);
      }
            
      return string.Format("#{0} doesn't exist.", ticket);
    }

    [WebMethod]
    public string GetStatusWorkA(string ticket)
    {
      if(Global.state.ContainsKey(ticket))
      {
        return string.Format("#{0} status: {1}", ticket, Global.state[ticket]);
      }
            
      return string.Format("#{0} doesn't exist.", ticket);
    }
  }
}
The Global class:
Collapse
namespace WebServiceA 
{
  public class Global : System.Web.HttpApplication
  {
    static public Hashtable state = null;

    protected void Application_Start(Object sender, EventArgs e)
    {
      state = Hashtable.Synchronized(new Hashtable());
      Trace.WriteLine(string.Format("[{0}]ServiceA.Application_Start", 
         GetHashCode()));
    }
    // ...

    protected void Application_End(Object sender, EventArgs e)
    {
      state.Clear();
      Trace.WriteLine(string.Format("[{0}]ServiceA.Application_End", 
         GetHashCode()));
    }
  }
}
The request and callback Web Methods run in the different sessions, that's why the state has to be saved in the Global class in the shareable resource (for instance; Hashtable). Each client's request has a unique ticket Id, which it is a key information (cookie) between the Web Services and global State.

2. Web Service B

This Web Service is very simple. There is only one Web Method to simulate some work. During this process, the service is invoking the callback Web Method. The job runs in the required number of loops in the sync manner. Each loop is invoking the Callback Web Method to the Web Service "A" and based on its return value the process can be aborted. The service can hold the State in the Session.
Collapse
namespace WebServiceB
{
  public class ServiceB : System.Web.Services.WebService
  {
    public ServiceB()
    {
      //CODEGEN: This call is required by the ASP.NET Web Services Designer

      InitializeComponent();
      Trace.WriteLine(string.Format("[{0}]ServiceB.ctor", GetHashCode()));
    }

    #region Component Designer generated code
    private void InitializeComponent()
    {
    }
    #endregion

    protected override void Dispose( bool disposing )
    {
    Trace.WriteLine(string.Format("[{0}]ServiceB.Dispose", GetHashCode()));
    }

    [WebMethod(EnableSession=true)]
    public string DoSomeWorkB(int count, string callbackWS, string stateWS)
    {
    Trace.WriteLine(string.Format("[{0}]ServiceB.DoSomeWorkB start...", 
            GetHashCode()));
      int startTC = Environment.TickCount;

      // async call to the ServiceA.CallbackServiceA method    

      WebServiceAccessor wsa = new WebServiceAccessor(callbackWS);
      object esObj = wsa.CreateInstance("ServiceA");

      // prepare the callback arguments: sender, EventArgs 

      string sender = GetType().FullName; 
      WebServiceEventArgs ea = new WebServiceEventArgs();
      ea.name = "This is a callback";
      ea.state = stateWS;

      for(int ii = 0; ii < (count & 0xff); ii++) // max. count = 255

      {
        ea.param = ii;
        string xmlEventArgs = ea;
        object retval = wsa.Invoke(esObj, "BeginCallbackServiceA", 
             sender, xmlEventArgs, null, null);
            
        // simulate some task

        Thread.Sleep(250);

        // Wait for the call to complete

        WebClientAsyncResult ar = retval as WebClientAsyncResult;
        ar.AsyncWaitHandle.WaitOne();

        // result

        object result = wsa.Invoke(esObj, "EndCallbackServiceA", ar);
        if((bool)result == false)
        {
          Trace.WriteLine(string.Format(
             "[{0}]ServiceB.DoSomeWorkB has been aborted", GetHashCode()));
          return string.Format("#{0} aborted during the progress of {1}.", 
             stateWS, ii);
        }
      }
      //

      int durationTC = Environment.TickCount - startTC;
      Trace.WriteLine(string.Format("[{0}]ServiceB.DoSomeWorkB done in {1}ms", 
                GetHashCode(), durationTC));
      return string.Format("#{0} done in {1}ms", stateWS, durationTC);
    }
  }
}

3. Web Form

This is a simple browser-based client to the Web Service "A". Each Web Method has own button event handler. The State is holding on the Session class in the Stack object. The page updating is based on the mechanism, where event handlers pushing data to the Session Stack and then during the Page_Load time they are pulling and inserting into the ListBox control. The other interesting issue for this client is asynchronously invoking the DoSomeWorkA Method, where process is yielded and handled by its callback, that's why we can send the other requests to the Web Service "A". Note that each job is identified by its ticket ID which it represents the key of the State.
Collapse
namespace WebFormCallbackWS
{
  public class WebForm1 : System.Web.UI.Page
  {
    // ...

    protected ServiceA sa = new ServiceA();
        
    public WebForm1()
    {
      Page.Init += new System.EventHandler(Page_Init);
    }

    private void Page_Load(object sender, System.EventArgs e)
    {
      if(IsPostBack == false) 
      {
        //initialize controls, one time!

        if(Session["Status"] == null)
          Session["Status"] = Stack.Synchronized(new Stack());
      }
      else
      {
        Stack stack = Session["Status"] as Stack;
        while(stack.Count > 0) 
          ListBoxCallbackStatus.Items.Add(stack.Pop().ToString());

        int numberOfItems = ListBoxCallbackStatus.Items.Count;
        if(numberOfItems > 13)
          ListBoxCallbackStatus.SelectedIndex = numberOfItems - 13;
      }
    }
    private void Page_Init(object sender, EventArgs e)
    {
      // ...

    }

    #region Web Form Designer generated code
    private void InitializeComponent()
    {   
      // ... 

    }
    #endregion

    // Call the web service asynchronously

    private void ButtonDoSomeWorkA_Click(object sender, System.EventArgs e)
    {
      int count = Convert.ToInt32(TextBoxCount.Text);
      string ticket = TextBoxTicketId.Text;
      //

      AsyncCallback callback = new AsyncCallback(callbackDoSomeWorkA);
      IAsyncResult ar = sa.BeginDoSomeWorkA(count, ticket, callback, null);
      ListBoxCallbackStatus.Items.Add(string.Format("#{0} start ...", 
           ticket));
    }
    // the call callback from the WebService

    private void callbackDoSomeWorkA(IAsyncResult ar) 
    {
      string retval = sa.EndDoSomeWorkA(ar);
      Stack stack = Session["Status"] as Stack;
      stack.Push(retval);
    }
    // call the web service 

    private void ButtonAbort_Click(object sender, System.EventArgs e)
    {
      Stack stack = Session["Status"] as Stack;
      stack.Push(sa.AbortWorkA(TextBoxTicketId.Text));

    }
    // Get the status from the web service 

    private void ButtonRefresh_Click(object sender, System.EventArgs e)
    {
      Stack stack = Session["Status"] as Stack;
      stack.Push(sa.GetStatusWorkA(TextBoxTicketId.Text));
    }
    // clean-up the listbox

    private void ButtonClear_Click(object sender, System.EventArgs e)
    {
      ListBoxCallbackStatus.Items.Clear();
    }
  }
}

Now it is the time to make a test. The above picture shows the Web Form user interface. Be sure you are running on-line and Local intranet. Click on the DoSomeWork button and then ask for Status. The Status or Abort buttons can be clicked any time. The ListBox control will display the current State of the specified job (by the ticket Id).

Conclusion

Using the Web Services in the Application model opening a new dimension of the distributed architectures. Dynamically calling the Web Methods in the business model hierarchy is straightforward and simply using the features of the .NET Framework. This article shown how it can be implemented using the C# language. The solution has been created to show a concept and design issues. The real production version needs to address issues such as security, full url addressing, password, server proxy, etc.

Useful Links