Posts tagged ‘Extension Methods’

Visual Studio 2010 - Beta 2

Whilst preparing for my Entity Framework 4.0 and Unit Testing presentation at the recent New England Code Camp, I came across an issue with my code that I couldn’t entirely understand. Picture the scenario:

In Visual Studio 2008 utilizing EF 1.0:

  • Create a repository Assembly that contains the model and entity classes (i.e. no POCO) for a group of tables in my DB
  • Create a Manager class to expose public functions / methods to retrieve data from the DB via the EntityContext
    [store the Manager.cs file in a common area and create a Link to it from the project].
  • Create an extension method that will handle the usage of Lambda expressions inside the context of .Include() [Did I say I hate "magic strings"?]
    [store the ObjectQueryExtension.cs file in a common area and create a Link to it from the project]
  • Create a simple Console app to call the methods on the Manager class
    [store the Program.cs file in a common area and create a Link to it from the project].

Visual Studio 2008

In Visual Studio 2010 Beta 2 utilizing EF 4.0:

  • Create a repository Assembly that contains the model and entity classes (i.e. no POCO) for a group of tables in my DB
  • Create a Manager class to expose public functions / methods to retrieve data from the DB via the EntityContext
    [create a Link to the Manager.cs file from the project in a common area].
  • Create an extension method that will handle the usage of Lambda expressions inside the context of .Include()
    [create a Link to the ObjectQueryExtension.cs file in the common area from the project]
  • Create a simple Console app to call the methods on the Manager class
    [create a Link to the Program.cs file in the common area from the project].

Visual Studio 2010 - Beta 2

As you will see, the only difference between the two solutions is the actual EntityFramework context definition; one utilizes EF 1.0 and the other EF 4.0.

Compile and execute both and it works perfectly, same Program.cs code for both (making the Console Application); same Manager.cs and ObjectQueryExtension.cs code for both (making the RepositoryManager assembly).

Now the fun starts. I then worked my way back to using Dependency Injection and created the unit test methods based on the VS 2010 project described above. When the compiler attempts to compile the following section of code:


IQueryable<Person> query = context.PersonSet
                               .Include(p => p.PersonalDetail)
                               .Include("FavoriteBeers.Beer")
                               .Include(p => p.Customer.Include<Customer, CustomerType>(c => c.CustomerType))
                               .Include(p => p.Addresses);

The following compile error was the result of the attempted compilation against the preceding code:


Ef4.0AndEf1.0\PocoInEF4.0\EFWorkshop.Poco.RepositoryManager\Manager.cs(78,53): error CS1660: Cannot convert lambda expression to type 'string' because it is not a delegate type
Ef4.0AndEf1.0\PocoInEF4.0\EFWorkshop.Poco.RepositoryManager\Manager.cs(78,58): error CS0311: The type 'EFWorkshop.Poco.Base.Entities.Customer' cannot be used as type parameter 'TSource' in the generic type or method 'EFWorkshop.Ef.Repository.ObjectQueryExtension.Include<TSource,TPropType>(TSource, System.Linq.Expressions.Expression<System.Func<TSource,TPropType>>)'. There is no implicit reference conversion from 'EFWorkshop.Poco.Base.Entities.Customer' to 'System.Data.Objects.DataClasses.IEntityWithRelationships'.
Ef4.0AndEf1.0\Common\ObjectQueryExtension.cs(118,33): (Related location)
Ef4.0AndEf1.0\PocoInEF4.0\EFWorkshop.Poco.RepositoryManager\Manager.cs(78,60): error CS1061: 'System.Linq.IQueryable<EFWorkshop.Poco.Base.Entities.Person>' does not contain a definition for 'Customer' and no extension method 'Customer' accepting a first argument of type 'System.Linq.IQueryable<EFWorkshop.Poco.Base.Entities.Person>' could be found (are you missing a using directive or an assembly reference?)

However, the following “magic string” laden Includes compile and execute fine:


IQueryable<Person> query = _context.People
    .Include("FavoriteBeers.Beer")
    .Include("PersonalDetail")
    .Include("Customer.CustomerType")
    .Include("Addresses");

Therefore it would seem that the non POCO based classes permit us having the Lambda expression based includes as described earlier; however the moment that POCO is introduced that style of Include is no longer viable – or is it?

  • Share/Bookmark

I love these discussions, simply because in most cases, in my opinion, it boils down to personal style. Recently I was discussing with a colleague the different ways a LINQ query could be written for the same result (the discussion was based on code for Entity Framework for .NET 3.5).

Consider the following code snippets; the first uses what has been coined as “Query Comprehension Syntax” and the second is based on Method Chains (also seen referred to as “Extension Methods”):

Query Copmrehension Syntax


var query = (from e in EmployeeSet
             from o in OrderSet
             from c in CustomerSet
             where e.EmployeeID == o.Employees.EmployeeID &&
                   o.Customers.CustomerID == c.CustomerID
             group e by new { EmployeeInstance = e, CustomerInstance = c } into result
             orderby result.Key.EmployeeInstance.LastName
             select result);

Method Chain (Extension Method) Syntax


var query = (EmployeeSet
    .SelectMany(e => OrderSet, (e, o) => new {e, o})
    .SelectMany(@t => CustomerSet,(@t, c) => new {@t, c})
    .Where(@t => @t.@t.e.EmployeeID == @t.@t.o.Employees.EmployeeID &&
                @t.@t.o.Customers.CustomerID == @t.c.CustomerID)
    .GroupBy(@t => new {EmployeeInstance = @t.@t.e, CustomerInstance = @t.c}, @t => @t.@t.e)
    .OrderBy(result => result.Key.EmployeeInstance.LastName));

Well? What is the difference?

In both cases they are trying to emulate the following SQL (albeit with a bit more beef – i.e. returning the complete entities – not just the strings):

[code lang="sql"]

select e.FirstName, e.LastName, c.ContactName
from Customers c, Employees e, Orders o
where e.EmployeeID = o.EmployeeID
and o.CustomerID = c.CustomerID
group by e.FirstName, e.LastName, c.ContactName
order by e.LastName

Ok. What is the difference?

Looking at the output we get the exact same result in both cases (when put through the following algorithm):


Dictionary<BaseEmployee, List<BaseCustomer>> retVal = new Dictionary<BaseEmployee, List<BaseCustomer>>();
string previousEmployeeName = string.Empty;
BaseEmployee previousEmployeeInstance = null;
foreach (var queryItem in query)
{
    BaseEmployee employee = ConvertToBase(queryItem.Key.EmployeeInstance);
    string currentEmployeeName = string.Format("{0}-{1}", employee.FirstName, employee.LastName);
    BaseCustomer customer = ConvertToBase(queryItem.Key.CustomerInstance);
    if (currentEmployeeName != previousEmployeeName)
    {
        previousEmployeeName = currentEmployeeName;
        retVal.Add(employee, new List<BaseCustomer>());
        previousEmployeeInstance = employee;
    }
    retVal[previousEmployeeInstance].Add(customer);
}

So? What is the difference?

And looking at it through SQL Profiler, guess what? That's right! We get exactly the same result - SQL is exactly the same!

Alright already! WHAT IS THE DIFFERENCE?

Really? It would appear to be - readability... In cases where the query is long and drawn out, most of us would be quite comfortable with the QCS format as it is closer to what we are accustomed to seeing in T-SQL; however the Method Chain (Extension Methods) format is much more compact for smaller queries - much neater in my mind.

The whole discussion came about because of the functionality of ReSharper to build the Method Chain. If you hover over a QCS LINQ query, the ReSharper "lightbulb" will appear to give you the following option:

ReSharper Convert To Methods Chain

And VOILA! You have now converted it to a Method Chain... the only gotcha is that you can't change it back (forgetting the use of CTRL + Z).

Similar discussions have appeared here and here on Stack Overflow.

<Update June 10, 2009>:I wrapped some timing around both syntaxes and this was the result:

First time run:

ExtensionMethod syntax: 00:00:01.0271109
QueryComprehensionSyntax syntax: 00:00:01.0216957

Subsequent runs

ExtensionMethod syntax: 00:00:00.0976207
QueryComprehensionSyntax syntax: 00:00:00.0917982
ExtensionMethod syntax: 00:00:00.0955029
QueryComprehensionSyntax syntax: 00:00:00.0908081
ExtensionMethod syntax: 00:00:00.0974213
QueryComprehensionSyntax syntax: 00:00:00.0970167
ExtensionMethod syntax: 00:00:00.0905830
QueryComprehensionSyntax syntax: 00:00:00.1007809
ExtensionMethod syntax: 00:00:00.0895348
QueryComprehensionSyntax syntax: 00:00:00.0965262
ExtensionMethod syntax: 00:00:00.0972316
QueryComprehensionSyntax syntax: 00:00:00.0886347
ExtensionMethod syntax: 00:00:00.1030211
QueryComprehensionSyntax syntax: 00:00:00.0927539
ExtensionMethod syntax: 00:00:00.0943354
QueryComprehensionSyntax syntax: 00:00:00.0919315
ExtensionMethod syntax: 00:00:00.0938015
QueryComprehensionSyntax syntax: 00:00:00.0908355
ExtensionMethod syntax: 00:00:00.0921636
QueryComprehensionSyntax syntax: 00:00:00.0972665

  • Share/Bookmark