Infosys Microsoft Alliance and Solutions blog

« March 2007 | Main | May 2007 »

April 30, 2007

VSTS (Orcas Beta 1) Profiler - Comparing Performance Reports

In my earlier blog I had looked at March CTP of Orcas and talked about performance reports and performance comparison reports and raised a few issues.

I downloaded and started to play around with Orcas Beta 1 today and thought of checking the performance comparison report to start with. I ran the same code as I did earlier and compared the reports.

The first thing I noticed is that the Performance Wizard and related menu options have moved under the new "Developer" menu. The highlighting for hot paths in the Call Tree view (incidently there is a "Current View" label also added before the drop down) has also changed.

The important change is the way the deltas are displayed. A negative delta between baseline report and new report, which signifies a reduction in execution time is now shown with downwards green arrow, while a positive delta is shown with upwards orange arrow (see the snap shot below). This is exactly opposite to what it was in March CTP.

OrcasBeta.JPG

I will explore this further and update this blog with the findings soon.

April 24, 2007

Where are ASP.NET 2.0 Website Project Properties Stored?

Earlier today someone asked me where are the project properties of ASP.NET 2.0 web site stored? This seemingly simple question proved lot more difficult to answer since there is no .CSPROJ file for the web site (I was working with C# and hence .CSPROJ).

I started by creating a new ASP.NET 2.0 Web site project (you can create this on HTTP or file system, the behavior is the same). After that I went to the project properties dialog and modified a few properties like in Start action group, I modified Specific page to some value dummy value and in the Server group I modifed to point to some custom server. Do note that the custom server option can't be set if you created the web site on HTTP.

After this I saved the solution and closed Visual Studio to ensure that all data will be written to disk. However I didn't find any .CSPROJ file in the folder where I had created the project. I decided to search the other paths I knew are used by Visual Studio to store data and first looked at "C:\Documents and Settings\atulg\My Documents\Visual Studio 2005\Projects".

I could see only the .SLN and .SUO files here and no sign of any .CSPROJ file. The SLN file had solution specific entries and the SUO had Visual Studio settings. I then searched the other location where typically files for ASP.NET Site are maintained and that is "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files". However this folder contains files related to code and compiled output and again there wasn't any sign of .CSPROJ file.

I searched the registry but no luck. The project settings were definitely being saved somewhere since I could reopen the web site in Visual studio and see the values as set by me. I copied the project to another machine and when I opened the web site there, I didn't see my custom values, but the default values. This just reconfirmed that the project settings were being saved some place on local machine, but where remained a mystery.

Finally I got an idea and decided to use FileSystemWatcher class to monitor file writes on my machine and hence locate where the information was being saved. I started with Path as "C:\" and NotifyFilter as "NotifyFilters.LastWrite". However when I put a watch on C:\, it resulted in a whole lot of events being generated and I couldn't essentially make out anything.

I suspected the information to be getting stored somewhere in the Documents and Settings folder so I narrowed the Path to "C:\Documents and Settings\atulg". This time, the data was more manageable and I saw writes happening to two files inside of "C:\Documents and Settings\atulg\Local Settings\Application Data\Microsoft\WebsiteCache".

One was to FileAttributes.xml inside another subfolder by the name of the solution and one was to Websites.xml. The FileAttributes.xml didn't contain data of interest, however the other one did and I was finally seeing the project properties data in the Websites.xml file as below

  <Website RootUrl="E:\Work\v3.0\ProjTestWebsite\" CacheFolder="ProjTestWebsite" startpage="whatis this.aspx" customserverurl="http://localhost/ThreadlTest/Default.aspx" addnewitemlang="Visual C#" vwdport="4103" enablevswebserver="false" startaction="1" _LastAccess="4/24/2007 2:18:35 PM"/>

The startpage and customserverurl attributes contained values that I had assigned via the project properties screen.

For those of you who work with Vista, the path for Websites.xml will be as "C:\Users\atulg\AppData\Local\Microsoft\WebsiteCache". 

Do note however, that the entries to this file are written when either Visual Studio is closed or you open another project/solution in it. Just by hitting Save or Save All won't write the project properties in the file.

April 23, 2007

LINQ and its linkage to new features in .NET Framework 3.5 – Part II,,,

In my previous blog “LINQ and its linkage to new features in .NET Framework 3.5 – Part I”, we have seen how a query can be expressed with clarity with object oriented programming notation and couple of code snippets that showed how a class can be extended through “extension” methods – a concept that is introduced in .NET Framework 3.5. It is really important to know that when the query gets executed at runtime. The following code snippet shows the usage of query expression.

IEnumerable<string> name = Names.OrderBy(s => s.Length)
                        .Where(s => s.Length >= 15)
                        .Select(s => s);

The query execution does not happen in this line of code, though it uses the assignment operation. Only when, looping is happening through the foreach statement of IEnumerable[] items, the query gets executed.

See the following code.

 

IEnumerable<string> name = Names.OrderBy(s => s.Length)
                        .Where(s => s.Length >= 15)
                        .Select(s => s);
            Names.SetValue("All's Well That Ends Well", 5);
           
            Console.WriteLine("Shakespeare's play - All's Well That Ends Well - is added into Names,,,");
            Console.WriteLine("=======================");
            foreach (string str in name)
            {
                Console.WriteLine("{0}", str);
            }

            Console.WriteLine("=======================");

After assigning the query expression to the type “IEnumerable<string>”, I changed the first element of “Names” to be a Shakespeare’s work – “All’s Well That Ends Well”. Then I do the looping for displaying the result that outputs only the Shakespeare’s works. The Console output now shows this play as well on the screen.

LINQ Part 02 Image

 

This dynamism is very essential when the application deals with highly flux data, on which query is executed.

However, if the changes to the source type happen with in looping logic, we can’t expect the query execution to reflect on those changes. In the following code snippet, change to “Names” is done from within the foreach construct. The output will not reflect this change. But, the updated change will be reflected in the console, in the second looping.

IEnumerable<string> name = Names.OrderBy(s => s.Length)
                        .Where(s => s.Length >= 15)
                        .Select(s => s);
            Names.SetValue("All's Well That Ends Well", 5);
           
Console.WriteLine("Shakespeare's play - All's Well That Ends Well - is added into Names,,,");


            foreach (string str in name)
            {
                if (string.Compare(str, "All's Well That Ends Well") == 0)
                {
                    Names.SetValue("Much Ado About Nothing", 8);
                    Console.WriteLine("Shakespeare's play - Much Ado    About Nothing - is added into Names,,,");
                }


                Console.WriteLine("{0}", str);


            }


            Console.WriteLine("======================");
            Console.WriteLine("During Second Run,,,");
            Console.WriteLine("======================");
            foreach (string str in name)
            {
                Console.WriteLine("{0}", str);
            }


            Console.WriteLine("=======================");


Console output

LINQ Part 02 Image 02

Now, another twist to this story is all is not really well if there are two static classes that implement the extension method with the same name. Let us assume that we need to build another static class “HenrikIbsenWords” that will have the extension method with the same name (“Select”) as that of “ShakespeareWorks”. That extension method is designed to select only Henrik Ibsen’s plays.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Linq.Expressions;
namespace System.HenrikIbsenQuery
{
    public static class HenrikIbsenWorks
    {
        public static IEnumerable<T> Select<T>(this IEnumerable<T> source, Func<T, string> selector)
        {
            string[] dramasList = {
                                       "The Pretenders",
                                       "Peer Gynt",
                                       "Emperor and Galilean",
                                       "A Doll's House",
                                       "An Enemy of the People",
                                       "The Wild Duck",
                                       "Hedda Gabler",
                                       "The Master Builder"                             
                                      
                                   };
            foreach (T item in source)
            {
                foreach (string str in dramasList)
                {
                    if (string.Compare(item.ToString().ToLower(), str.ToLower()) == 0)
                        yield return item;
                }
            }
        }
    }

}

And if we need to use them interchangeably in the code, by importing namespaces of both those classes (as given in the following code snippet), we will get the following error message during build time.

The call is ambiguous between the following methods or properties:

'System.HenrikIbsenQuery.HenrikIbsenWorks.Select<string>(System.Collections.Generic.IEnumerable<string>, System.Linq.Func<string,string>)'

and

'System.ShakespeareQuery.ShakespeareWorks.Select<string>(System.Collections.Generic.IEnumerable<string>, System.Linq.Func<string,string>)'        

Code Snippet

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.ShakespeareQuery;
using System.HenrikIbsenQuery;
using System.MyStringExtensionNS;
namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] Names = {   "Waiting for Godot",
                                 "Death of a Salesman",
                                 "Rosencrantz and Guildenstern Are Dead",
                                 "The Devil's Disciple",
                                 "Mrs. Warren's Profession",
                                 "Arms and the Man",
                                 "Candida",
                                 "You Never Can Tell",
                                 "Caesar and Cleopatra",
                                 "Man and Superman",
                                 "Major Barbara",
                                 "The Doctor's Dilemma",
                                 "Pygmalion",
                                 "My Fair Lady",
                                 "The Pretenders",
                                 "Peer Gynt",
                                 "Emperor and Galilean",
                                 "A Doll's House",
                                 "An Enemy of the People",
                                 "The Wild Duck",
                                 "Hedda Gabler",
                                 "The Master Builder",
                                 "Hamlet",
                                 "Macbeth",
                                 "Othello",
                                 "Venus and Adonis",
                                 "A Midsummer Night's Dream",
                                 "As You Like It",
                                 "Henry VI Part I",
                                 "Henry V",
                                 "Henry IV Part I",
                                 "Henry VIII",
                                 "Richard II",
                                 "Richard III"                                 
                             };
           
           
            IEnumerable<string> name = Names.OrderBy(s => s.Length)
                        .Where(s => s.Length > 0)
                        .Select(s => s);
                        
                       
            foreach (string str in name)
            {
                Console.WriteLine("{0}", str);
            }
            Console.WriteLine("=======================");
            Console.Read();
        }
    }
}
If more than one namespace that are imported into a code, contain extension methods with the same name, it will create the ambiguity problem as we have just seen. But those namespace may contain some other methods that may need to be accessed by our code. In such circumstances, we can mitigate this ambiguity issue through the usage of namespace alias.
In the following code snippet, I have ensured that the extension method of “HenrikIbsenWork” class that is in “System.HenrikIbsenQuery” namespace is being applied to the “Names” type.
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using Shakespeare = System.ShakespeareQuery;
using System.HenrikIbsenQuery;
using System.MyStringExtensionNS;
namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] Names = {   "Waiting for Godot",
                                 "Death of a Salesman",
                                 "Rosencrantz and Guildenstern Are Dead",
                                 "The Devil's Disciple",
                                 "Mrs. Warren's Profession",
                                 "Arms and the Man",
                                 "Candida",
                                 "You Never Can Tell",
                                 "Caesar and Cleopatra",
                                 "Man and Superman",
                                 "Major Barbara",
                                 "The Doctor's Dilemma",
                                 "Pygmalion",
                                 "My Fair Lady",
                                 "The Pretenders",
                                 "Peer Gynt",
                                 "Emperor and Galilean",
                                 "A Doll's House",
                                 "An Enemy of the People",
                                 "The Wild Duck",
                                 "Hedda Gabler",
                                 "The Master Builder",
                                 "Hamlet",
                                 "Macbeth",
                                 "Othello",
                                 "Venus and Adonis",
                                 "A Midsummer Night's Dream",
                                 "As You Like It",
                                 "Henry VI Part I",
                                 "Henry V",
                                 "Henry IV Part I",
                                 "Henry VIII",
                                 "Richard II",
                                 "Richard III"                                
                             };
           
           
            IEnumerable<string> name = Names.OrderBy( s => s.Length )
                                        .Where( s => s.Length > 0 )
                                        .Select( s => s );
                                     
            foreach (string str in name)
                Console.WriteLine("{0}", str);
            Console.WriteLine("=======================");  
            Console.Read();
        }
    }
}

Console ouput

LINQ Part 02 Image

We will see the details about LINQ over database and XML in future blogs.

Loading Multiple Versions of same Assembly

The other day a colleague asked if there was a way he could work with different versions of the same assembly in his code i.e. could he invoke say Method A on Version 1.0 and Method B on Version 2.0 of the same assembly?

I was initially surprised as to why he would want to do that? People ask about being able to automatically load newer version of the assembly with the application or in case of .net being able to load an assembly built with .net framework 1.1 in an application built with .net framework 2.0, but usually not this.

When he explained his situation it made sense. He was working with Sharepoint Server API and there have been some changes in the API between SPS 2003 and 2007. So there was a need to invoke some functionality using SPS 2003 assembly and some functionality using the newer version available with SPS 2007.

I can think of another case where you have programmed against say a buggy assembly and have written some workaround logic for the bug in your code. With the newer version of that assembly that bug has been fixed, but due to some constraints, you can't remove your workaround code and hence can't use the new version. There is however new functionality provided by this new version which you do want to use. Hence you would need to be able to call new methods on new version and the buggy method on the old version.

With this preamble, lets get into how to get this done. There are various permutations and combinations and I will address them. At a high level the solution is in working with one version as already programmed (typically using early binding) and with another version using reflection. Do also note that the assembly loading follows probing rules as defined in .NET SDK documentation. To keep things simple, I will keep the folder path simple and not get into such diversions.

Consider this extremely critical business logic inside of a uniquely named assembly - ClassLibrary1.dll as below. Again to keep things simple, I am not showing the various "using" statements with the code. Additionally to identify which version the method is called on, I return the version# in the string itself. I can do a dynamic query for assembly version and remove this hard coding, but the recall.. our moto is simplicity at this time and focus on loading different versions and not any other aspect, hence the hard coding.

    public class Class1
    {
        public static string Method1()
        {
            return "Method 1 called on version 1.0.0.0";
        }
    }

Then there is this Winform application that invokes this static Method1 and displays the returned string in message box. To help identify if this call has happened due to early binding or reflection, I prefix the string accordingly. I build the code with version 1.0.0.0 of ClassLibrary1.dll and run the application. On invoking the method, I see a message box displaying the appropriate version#. Before we get into multiple versions, a quick discussion of what happens with private as against strong named assemblies is in order.

Weakly named library assembly (privately deployed)

If you update the version of such an assembly and redeploy it with the application, the application is able to load it without any issues. The change in just minor version to 1.1.0.0 or major version also to 2.0.0.0 doens't impact and the application works fine. Note that this is in line with basics of versioning that aren't applicable to weakly named assemblies.

Strongly named library assembly (privately deployed)

If you update the version of such an assembly and redeploy it with the applicaton, the application will give a runtime error when it tries to invoke a method of the library assembly. Due to strong naming, the version policy comes into effect and the application tries to invoked method on version against which it was complied, in this case 1.0.0.0. When it doens't finds this at runtime, it will give an error. It won't automatically start using 1.1.0.0 version. For that you need to specify the
bindingRedirect in the application configuration file.

There are multiple ways to create this configuration file. One is to do this with Visual studio itself and when the application is compiled, the config file is also complied and deployed along wih it. However if you aren't sure of the exact syntax, you can use the Microsoft .NET Framework 2.0 Configuration MMC from Administrators group. Via this you can add the specific application for configuration, then configure the specific library assembly and specify the binding policy. The configuration file generated will look something like this

<?xml version="1.0"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="ClassLibrary1" publicKeyToken="fbc28d9ca2fc8db5" />
        <publisherPolicy apply="no" />
        <bindingRedirect oldVersion="1.0.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>


To reiterate, this bindingRedirect is required only for strongly named assemblies. Once this configuration setting is in place, the application is able to load the 1.1.0.0 assembly and invoke methods on it. The same logic will work for assemblies deployed in GAC.

Let us now get on with loading of different versions of the same assembly. See the sample code below on how to achieve it.

        private void btn_Click(object sender, EventArgs e)
        {
            string str = "early bind - ";
            str += ClassLibrary1.Class1.Method1();
            MessageBox.Show(str);
 
            Assembly al = Assembly.LoadFile(@"E:\Temp\old\ClassLibrary1.dll");
 
            Type t = al.GetType("ClassLibrary1.Class1");
            MethodInfo m = t.GetMethod("Method1");
            str = "reflection - " + (string)m.Invoke(null, null);
            MessageBox.Show(str);
        }

When we run the application, we expect the early binding call to go to the newer version and the call via reflection to go to the older version, that we have specifically deployed in another sub-folder. We are using Assembly.LoadFile method so that we can specify a path.

One important aspect to note here is that the application will have to be compiled with the newer version of the assembly if the assembly is strongly named. If that isn't done, as discussed earlier, we will have to use a bindingRedirect configuration setting. Such setting works for all assembly load calls and will even redirect the assembly load call that we are doing via reflection and hence we will not be able to invoke methods on older version. By explicitly building the application with newer version, we don't need to the bindingRedirect configuration setting and our reflection call to earlier version will then work.

Loading assemblies from GAC

There is slight difference in the way the assembly is loaded via reflection if working against GAC. To successfully load different versions from GAC it is best to use the AssemblyName class and specify the FullName of the assembly you want to load. As we all know, FullName includes name, version number, culture and public key token.

        private void btn_Click(object sender, EventArgs e)

        {

            string str = "early bind - ";

            str += ClassLibrary1.Class1.Method1();

            MessageBox.Show(str);

 

            AssemblyName asm = new AssemblyName("ClassLibrary1, Version=1.1.0.0, Culture=neutral, 
                                                                                            PublicKeyToken=fbc28d9ca2fc8db5"
);

            Assembly al = Assembly.Load(asm);

 

            Type t = al.GetType("ClassLibrary1.Class1");

            MethodInfo m = t.GetMethod("Method1");

            str = "reflection - " + (string)m.Invoke(null, null);

            MessageBox.Show(str);

        }

Needless to say that it also possible to load as many versions as you want using the option of reflection and you need not invoke any version via early binding. I have used to early binding option just to highlight a case where you are already working with a version and want to also specifically invoke methods on a different version. This also means that you can early bind to an older version and load the newer version by reflection.

While playing around with this, I also realized that one needs not worry about references added to the project. The compiler is intelligent enough to add them to the manifest only if a call is made to any method contained in the referenced assembly. If no call is made, the reference isn't included and hence the assembly won't be loaded at run time. You can easily verify this by viewing the manifest via ILDasm utility. 

 

 


 

April 20, 2007

Inside Vista Kernel

Most people, when hear Vista, think about Aero glass affect, great new set of icons and wallpapers and UAC. However there is more to Vista than this. Mark Russinovich in 3 part series on Technet Magazine covers key features of Vista Kernel. Gives good insight into the features and make really interesting reading. Check them out

  1. Part 1
  2. Part 2
  3. Part 3

VSTS (Orcas March CTP) Profiler - Comparing Performance Reports

I recently came across this blog by Ianhu and was interested to explore the comparsion of performance reports.

Being able to compare reports from two performance runs is very important since without it, it becomes very difficult to figure out if one has been able to make improvements or not post the code changes. Hence I created a dummy application that created an array of 200000 strings and then wrote it on the Console. I wanted to see the impact of using an object based ArrayList or a generics based List<T>.

Post the capturing of execution data, I started to look at the report generated. The UI for this has been changed a bit and considering this is March CTP, this may change further. I will hence not get too much into the UI apart from a few quick observations

  1. There is new query option with which you can search for specific items of interest in the report
  2. The various tabs for Summary, Functions, Call Tree, etc have now become drop down items
  3. The earlier Functions view is now same as Modules view. However Functions view is still there, but it no longer shows the parent modules. That is shown in the new Modules tab.
  4. There is a new view for displaying Marks. These can be useful to take a snapshot during the execution for some interesting events. By default it captures the start and end of the performance test session.
  5. There are new views for Process data and for showing the Event tracing for Windows (ETW). See more information on ETW on MSDN and Matt Pietrek's blog. The ETW view doesn't seem to work in March CTP so I couldn't really see what all values it will display.

An interesting addition to the Call Tree view is the support for finding and highlighting hot paths. This is very handy to quickly find out key areas that are most time consuming and can help shortlist areas to explore for performance tuning. The following figure shows how "hot path" look like (with highlighting enabled).


hotspots.JPG

To view hot paths, in the Call Tree view, collapse all the nodes below, keep focus on the top node and hit the "Expand Hot Path" icon in the toolbar.

Coming back to the comparison report. This shows how the time spent in the functions has changed between the two reports and there is visual indication of an increase in time or decrease in time, as seen in the following figure.

comparison.jpg 

There are however few issues that I have with this comparison report and maybe things will change by the time RTM is available.

1. A function taking more time has a positive delta and appears with green upward arrow (as seen in the figure above). This seems to be wrong since when I do performance tuning, I would expect time taken the function to come down.

2. Individual Function time comparison doesn't makes much sense. The comparison should really happen on the overall time basis. Consider a case where a function though is taking more time, but has impacted another function to take far less time and thus giving an overall improvement in the application's performance. This isn't easily understood in the comparison report.

3. In the Functions view, the "Elapsed Inclusive time" shows time spent in that function and all its children. If we look at the application's entry point function, we will get an idea of overall time it has taken for the code to execute. If this time decreases in the second run, it would mean an improvement in the performance. However the comparison report still shows this with negative delta (orange down arrow). There should probably be a mechanism to show overall time comparison for the entry point method separately.

4. In the current version there seems to be some problem in displaying the "Elapsed Inclusive time" comparisons. The values shown in old and new value columns didn't match up with the ones in the baseline and new report. Not only they didn't match, they seemed to be too high.

As I explore this further, I will post more entries here. If you have any comments/observations to share, do post your comments here.

April 19, 2007

LINQ and its linkage to new features in .NET Framework 3.5 – Part I,,,

Language Integrated Query (LINQ) related changes are going to be significant ones in .NET Framework 3.5 that is going to be released with next version of VS – code named Orcas. .NET Framework 3.5 enables the developers to use the managed languages (C# Version 3.0 and VB Version 9.0) to define the query on relational database, hierarchical infosets such as XML or .NET collection objects such as Arrays, in highly expressive manner. LINQ is meant to give the power to the mainstream development community in dealing with data on various sources, in consistent fashion within the domain of the managed languages using the familiar and flexible object oriented programming notations.

To get the taste of it, see the following code snippet, which queries the play’s titles that exceeds 14 characters in length.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] Names = {   "Waiting for Godot",
                                 "Death of a Salesman",
                                 "Rosencrantz and Guildenstern Are Dead",
                                 "The Devil's Disciple",
                                 "Mrs. Warren's Profession",
                                 "Arms and the Man",
                                 "Candida",
                                 "You Never Can Tell",
                                 "Caesar and Cleopatra",
                                 "Man and Superman",
                                 "Major Barbara",
                                 "The Doctor's Dilemma",
                                 "Pygmalion",
                                 "My Fair Lady",
                                 "The Pretenders",
                                 "Peer Gynt",
                                 "Emperor and Galilean",
                                 "A Doll's House",
                                 "An Enemy of the People",
                                 "The Wild Duck",
                                 "Hedda Gabler",
                                 "The Master Builder",
                                 "Hamlet",
                                 "Macbeth",
                                 "Othello",
                                 "Venus and Adonis",
                                 "A Midsummer Night's Dream",
                                 "As You Like It",
                                 "Henry VI Part I",
                                 "Henry V",
                                 "Henry IV Part I",
                                 "Henry VIII",
                                 "Richard II",
                                 "Richard III"                                
                             };
          
            IEnumerable<string> name = from s in Names
                                       orderby s.Length
                                       where s.Length >= 15
                                       select s;
            foreach (string str in name)
                Console.WriteLine("{0}", str);
           
            Console.WriteLine("=======================");           
            Console.Read();
        }
    }
}

The output of the query is

Output console

The query expression that achieves this result is underlined in the following code snippet.

IEnumerable<string> name = from s in Names
                           orderby s.Length
                           where s.Length >= 15
                           select s;

LINQ provides T-SQL kind of outlook to the C# code. Though it exactly look like query statements, internally this piece of code will be converted into the following .NET notation format.

IEnumerable<string> name = Names.OrderBy(s => s.Length)
                                          .Where(s =>  s.Length >= 15)

                                          .Select(s => s);

This query expression is made up of standard query operators like Select, Where and OrderBy. There are many such standard operators like  SelectMany, Count, All, Any, Reverse, Distinct, Range, Repeat, etc are defined in System.Linq’s Enumerable class. These query operators are defined as the static methods in Enumerable class which is a static one. However, these methods are bit unique from being normal static methods. First of all, these methods can be invoked similar to instance method. And the first parameter to this static method mandatorily identifies the type to which it is attached. Basically, this static method is to extend certain logic of a specific type from the outside of that type. For example, all those above said standard query operators are extension methods over IEnumerable<T> object, but they are implemented as the static methods to the static class – Enumerable in System.Linq namespace. These extension methods are backbones for implementing highly expressive query syntaxes in managed languages.

The following code snippets show how these three - Select, Where and OrderBy -standard query operators are defined as Extension methods in Enumerable class.

Select query operator definition in C#

public static IEnumerable<TResult> Select<TSource,TResult> (
                                  IEnumerable<TSource> source, Func<TSource,TResult> selector)

Select query operator definition in VB.NET

Public Shared Function Select(Of TSource, TResult) ( _
      source As IEnumerable(Of TSource), _
      selector As Func(Of TSource, TResult) ) As IEnumerable(Of TResult)

Where query operator definition in C#

public static IEnumerable<TSource> Where<TSource> (
      IEnumerable<TSource> source,
      Func<TSource,bool> predicate )

Where query operator definition in VB.NET

Public Shared Function Where(Of TSource)( _
    ByVal source As IEnumerable(Of TSource), _
    ByVal predicate As Func(Of TSource, Boolean) _
) As IEnumerable(Of TSource)

OrderBy query operator in C#

public static IOrderedSequence<TSource> OrderBy<TSource,TKey> (
      IEnumerable<TSource> source,
      Func<TSource,TKey> keySelector )

OrderBy query operator in VB.NET

Public Shared Function OrderBy(Of TSource, TKey)( _
    ByVal source As IEnumerable(Of TSource), _
    ByVal keySelector As Func(Of TSource, TKey) ) As IOrderedSequence(Of TSource)

Let us observe the extension method definition of “Select”.The first parameter “IEnumerable<TSource> source” lets this method to attach itself to “IEnumerable<T>” type. It also means that this method can be invoked on the instance of the type “IEnumerable<TSource>“. The second parameter is nothing but a delegate type and its equivalent delegate syntax is as given below.

public delegate TResult Func<TArg0,TResult> (
     
)

[Delegate definition in VB.NET is

Dim instance As New Func(Of TArg0, TResult)(AddressOf HandlerMethod)

]

To understand the underlying concepts clearer, let us create an extension method for the standard query operator – Select in one of our custom static class – ShakespeareWorks. Then let us use our custom extension method (or our custom standard query operator) to override the standard query operator that is defined on System.Linq’s Enumerable class.

In the custom standard query operator, I implemented the logic so that it will select only the works of Shakespeare. The following code snippet provides the definition of the extension method. The keyword “this” in the underlined portion of the code is mandatory as it allows the instance of type IEnumerable<T> to invoke this method.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace System.ShakespeareQuery
{
    public static class ShakespeareWorks
    {
public static IEnumerable<T>  Select<T>(this IEnumerable<T> source, Func<T, string> selector)
      {
            string[] dramasList = {
                                       "Comedy of Errors",
                                       "Titus Andronicus",
                                       "Pericles",
                                       "Cymbeline",
                                       "All's Well That Ends Well",
                                       "Taming of the Shrew",
                                       "Love's Labour's Lost",
                                       "Merchant of Venice",
                                       "Romeo and Juliet",
                                       "Henry IV part I",
                                       "Henry IV part II",
                                       "Henry V",
                                       "Richard II",
                                       "Richard III",
                                       "Henry VI Part I",
                                       "Henry VI Part II",
                                       "Henry VI Part III",
                                       "Henry VIII",
                                       "Much Ado About Nothing",                       
                                       "As You Like It",
                                       "Julius Caesar",
                                       "Hamlet",
                                       "King John",
                                       "Othello",
                                       "Twelfth Night",
                                       "The Two Gentleman of Verona",
                                       "Macbeth",
                                       "King Lear",
                                       "The Winter's Tale",                                       
                                       "A Midsummer Night's Dream",
                                       "The Merchant of Venice",
                                       "Merry Wives of Windsor",
                                       "Troilus and Cressida",
                                       "Measure for Measure",
                                       "Coriolanus",
                                       "Antony and Cleopatra",
                                       "Timon of Athens",
                                       "The Tempest",
                                       "The Two Noble Kinsmen"
                                   };
            foreach (T item in source)
            {
                foreach (string str in dramasList)
                {
                    if (string.Compare(selector(item).ToLower(), str.ToLower()) == 0)
                        yield return item;
                }
            }
        }
      
    }
}

In order to use our custom query operator, we have to refer the namespace “System.ShakespeareQuery” (by importing that through “using”) in our console application wherein we are going to write query on the play’s titles.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.ShakespeareQuery;
namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] Names = {   "Waiting for Godot",
                                 "Death of a Salesman",
                                 "Rosencrantz and Guildenstern Are Dead",
                                 "The Devil's Disciple",
                                 "Mrs. Warren's Profession",
                                 "Arms and the Man",
                                 "Candida",
                                 "You Never Can Tell",
                                 "Caesar and Cleopatra",
                                 "Man and Superman",