TheChaseMan's Frenetic SoapBox

Always looking for better ways to do things...

DLINQ: Initial Observations Part 2

To continue on my last blog post regarding DLinq, I brought up the issue of "what will the actual T-SQL query look like when you do <x>." Specifically, if you do a query like this to get customer data...
 

var custs =

       from c in Customers

       where c.CompanyName.StartsWith("a")

       select c;

Now let's say you take the above DLinq query and then reference an associated entity using an aggregate function like so...

Console.WriteLine("ID={0}, CompanyName={1}, NumOrders={1}", cust.CustomerID, cust.CompanyName, cust.Orders.Count);

You end up with 1 query for customers and then 1 query for *each* associated entity reference (cust.Orders.Count) - yikes, not a good thing! If I run that Console.WriteLine command in a loop, that's one query per iteration. But this is transparent to the C# developer, so SQL Profiler can be your friend! (Note: take a look at my last blog post to see what SQL Profiler reported based on the above Dlinq query).
 

So, let's refactor that code into something that will make a single database trip that will give us all of the info we need....
 

var custs =

    from c in Customers

    where c.CompanyName.StartsWith("A")

    select new {

        c.CustomerID,

        c.CompanyName,

        OrdersForCust =

            c.Orders.Count

    };

 

foreach(var cust in custs)

{

    Console.WriteLine("ID={0}, CompanyName={1}, NumOrders:{2}",

         cust.CustomerID,

         cust.CompanyName,

         cust.OrdersForCust);

}



SQL Profiler tells me that a single query (w/ subquery) was executed as follows:
 
SELECT [t0].[CompanyName],
[t0].[CustomerID],
(
    SELECT COUNT(*) AS [C0]
    FROM [Orders] AS [t1]
    WHERE [t1].[CustomerID] = [t0].[CustomerID]
) AS [OrdersForCust]
FROM [Customers] AS [t0]
WHERE [t0].[CompanyName] LIKE 'A%'

Much nicer!

Well, now it's time for me to fess up. Refactoring out the grouping problem into an anonymous type took me several attempts, which hurt a little because I pride myself with being able to write some pretty heinous queries with T-SQL. But as Aaron Skonnard points out, this is a lot of stuff to take in at once (anonymous types, Linq syntax, lambdas even though I'm not using any, etc). Interestingly enough, I was able to write the same query using the LLBLGen Pro object model (which is definitely more verbose) in less time....

 

using (DataAccessAdapter adapter = new DataAccessAdapter())

using (DataTable table = new DataTable())

{

    ResultsetFields fields = new ResultsetFields(3);

    fields.DefineField(CustomersFieldIndex.CustomerId, 0, "CustomerId");

    fields.DefineField(CustomersFieldIndex.CompanyName, 1, "CompanyName");

    fields.DefineField(OrdersFieldIndex.OrderId, 2, "NumOrders");

    fields[2].AggregateFunctionToApply = AggregateFunction.Count;

 

    RelationPredicateBucket filter = new RelationPredicateBucket();

    filter.Relations.Add(CustomersEntity.Relations.OrdersEntityUsingCustomerId);

    IPredicateExpression filterExpression = new PredicateExpression();

    filterExpression.Add(PredicateFactory.Like(CustomersFieldIndex.CompanyName, "A%"));

    filter.PredicateExpression.Add(filterExpression);

 

    IGroupByCollection groupByClause = new GroupByCollection();

    groupByClause.Add(fields[0]);

    groupByClause.Add(fields[1]);

    adapter.FetchTypedList(fields, table, filter, 0, null, true, groupByClause);

}

Thankfully C# 3.0 won't be out for a while so I have time to digest all of this stuff.  :-)


Digg!

posted on Tuesday, September 20, 2005 8:47 PM

Feedback

# No Luck with the LinqWinFXApplication template in VS2005 Beta 2 10/9/2005 7:47 PM TheChaseMan's Frenetic SoapBox