开发者

Performing simultaneous unrelated queries in EF4 without a stored procedure?

I have a page that pulls together aggregate data from two different tables. I would like to perform these queries in parallel to reduce the latency w开发者_StackOverflowithout having to introduce a stored procedure that would do both.

For example, I currently have this:

ViewBag.TotalUsers = DB.Users.Count();
ViewBag.TotalPosts = DB.Posts.Count();
// Page displays both values but has two trips to the DB server

I'd like something akin to:

var info = DB.Select(db => new { 
   TotalUsers = db.Users.Count(), 
   TotalPosts = db.Posts.Count());
// Page displays both values using one trip to DB server.

that would generate a query like this

SELECT (SELECT COUNT(*) FROM Users) AS TotalUsers, 
       (SELECT COUNT(*) FROM Posts) AS TotalPosts

Thus, I'm looking for a single query to hit the DB server. I'm not asking how to parallelize two separate queries using Tasks or Threads

Obviously I could create a stored procedure that got back both values in a single trip, but I'd like to avoid that if possible as it's easier to add additional stats purely in code rather than having to keep refreshing the DB import.

Am I missing something? Is there a nice pattern in EF to say that you'd like several disparate values that can all be fetched in parallel?


This will return the counts using a single select statement, but there is an important caveat. You'll notice that the EF-generated sql uses cross joins, so there must be a table (not necessarily one of the ones you are counting), that is guaranteed to have rows in it, otherwise the query will return no results. This isn't an ideal solution, but I don't know that it's possible to generate the sql in your example since it doesn't have a from clause in the outer query.

The following code counts records in the Addresses and People tables in the Adventure Works database, and relies on StateProvinces to have at least 1 record:

var r = from x in StateProvinces.Top("1")
        let ac = Addresses.Count()
        let pc = People.Count()
        select new { AddressCount = ac, PeopleCount = pc };

and this is the SQL that is produced:

SELECT 
      1 AS [C1], 
      [GroupBy1].[A1] AS [C2], 
      [GroupBy2].[A1] AS [C3]
FROM    
    (
        SELECT TOP (1) [c].[StateProvinceID] AS [StateProvinceID] 
        FROM [Person].[StateProvince] AS [c] 
    ) AS [Limit1]
CROSS JOIN  
    (
        SELECT COUNT(1) AS [A1] 
        FROM [Person].[Address] AS [Extent2] 
    ) AS [GroupBy1]
CROSS JOIN  
    (
        SELECT COUNT(1) AS [A1] 
        FROM [Person].[Person] AS [Extent3] 
    ) AS [GroupBy2]

and the results from the query when it's run in SSMS:

C1          C2          C3
----------- ----------- -----------
1           19614       19972


You should be able to accomplish what you want with Parallel LINQ (PLINQ). You can find an introduction here.


It seems like there's no good way to do this (yet) in EF4. You can either:

  1. Use the technique described by adrift which will generate a slightly awkward query.
  2. Use the ExecuteStoreQuery where T is some dummy class that you create with property getters/setters matching the name of the columns from the query. The disadvantage of this approach is that you can't directly use your entity model and have to resort to SQL. In addition, you have to create these dummy entities.
  3. Use the a MultiQuery class that combines several queries into one. This is similar to NHibernate's futures hinted at by StanK in the comments. This is a little hack-ish and it doesn't seem to support scalar valued queries (yet).
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜