3

I am using EF 4.0 POCO in my application. Are there any downsides to retrieving information like this?

Given a customerId and a productId, I would like to apply some business rules that require me to fetch lots teeny pieces of information from the database requiring multiple queries. Instead, I could write one query like so:

var customerId = 1;
var productId = 1;

var aggregateQuery = 
    from entry in Customers.Take(0).DefaultIfEmpty()
    select new
    {
        numberOfOrders = SalesOrderHeaders.Where (header => header.CustomerID == customerId).Count(),
        canSellProduct = Products.Where(product => product.ProductID == productId && product.SellEndDate > DateTime.Now).Count () > 0

        //more infromation of this sort, required to enforce business rules
    };

var informationPacket = aggregateQuery.First();

The Customers.Take(0).DefaultIfEmpty() just gives a way to start the query and Customers, SalesOrderHeaders and Products are EF ObjectQuery instances from the context (This example if from LinqPad). This results in the following SQL:

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 1
DECLARE @p2 DateTime = '2012-04-04 21:02:20.798'
DECLARE @p3 Int = 0
-- EndRegion
SELECT TOP (1) [t6].[value] AS [numberOfOrders], [t6].[value2] AS [canSellProduct]
FROM (
    SELECT (
        SELECT COUNT(*)
        FROM [Sales].[SalesOrderHeader] AS [t3]
        WHERE [t3].[CustomerID] = @p0
        ) AS [value], 
        (CASE 
            WHEN ((
                SELECT COUNT(*)
                FROM [Production].[Product] AS [t5]
                WHERE ([t5].[ProductID] = @p1) AND ([t5].[SellEndDate] > @p2)
                )) > @p3 THEN 1
            WHEN NOT (((
                SELECT COUNT(*)
                FROM [Production].[Product] AS [t5]
                WHERE ([t5].[ProductID] = @p1) AND ([t5].[SellEndDate] > @p2)
                )) > @p3) THEN 0
            ELSE NULL
         END) AS [value2]
    FROM (
        SELECT NULL AS [EMPTY]
        ) AS [t0]
    OUTER APPLY (
        SELECT TOP (0) NULL AS [EMPTY]
        FROM [Sales].[Customer] AS [t1]
        ) AS [t2]
    ) AS [t6]
Raghu Dodda
  • 1,505
  • 1
  • 21
  • 28

1 Answers1

1

I lean to using separate queries for three reasons:

  • Isolation: Separate queries are much clearer and better maintainable: with one monolith query, each change potentially has many side-effects. It is easier to apply business rules to small, isolated pieces of code.
  • Efficiency: You might end up composing a query that is far less inefficient than separate queries because it gets impossible to find a good execution plan, this may even outweigh the cost of more database round-trips (but that is to be benchmarked).
  • Locked-in: It may work for a while, until requirements change in a way that one big query does not work anymore: may require a disproportional lot of refactoring.

Plus a bit of gut feeling: needing some trick (Take(0)) is often indicative of bad design (or maybe you're just f** brilliant, but in my case it's usually the former).

However, I see the potential advantages of course. As said, it may turn out to perform better because of less db roundtrips. And it is quite comfortable to use one select new to compose one data transfer object as opposed to knitting it together from the separate bits.

So, not a clear cut verdict. Personally I like to keep things simple and deal with performance when it really is an issue.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • It is definitely not clean code, and it is certainly hard to parse the intent of Take(0) without prior knowledge. In this case, it is the trade-off between maintainability and performance (we have one web-request doing 8-10 db calls). I wish EF had something cleaner where you can batch queries or something. – Raghu Dodda Apr 07 '12 at 03:16
  • 1
    NHibernate can do batch queries, sigh... Maybe EF will catch up one day. If the web request is the bottleneck (which is not necessarily always the case) you probably should do it your way. Comment your code extensively. – Gert Arnold Apr 07 '12 at 09:23