Cameron
Cameron

Reputation: 2594

Convert SQL to EF Linq

I have the following query:

SELECT COUNT(1)
FROM   Warehouse.WorkItems wi
WHERE  wi.TaskId = (SELECT TaskId
                    FROM   Warehouse.WorkItems
                    WHERE  WorkItemId = @WorkItemId)
  AND  wi.IsComplete = 0;

And since we are using EF, I'd like to be able to use the Linq functionality to generate this query. (I know that I can give it a string query like this, but I would like to use EF+Linq to generate the query for me, for refactoring reasons.)

I really don't need to know the results of the query. I just need to know if there are any results. (The use of an Any() would be perfect, but I can't get the write code for it.)

So... Basically, how do I write that SQL query as a LINQ query?

Edit: Table Structure

WorkItemId - int - Primary Key
TaskId - int - Foreign Key on Warehouse.Tasks
IsComplete - bool
JobId - int 
UserName - string
ReportName - string
ReportCriteria - string
ReportId - int - Foreign Key on Warehouse.Reports
CreatedTime - DateTime

Upvotes: 2

Views: 216

Answers (4)

Cetin Basoz
Cetin Basoz

Reputation: 23837

Subquery in the original SQL was a useless one, thus not a good sample for Any() usage. It is simply:

SELECT COUNT(*)
FROM   Warehouse.WorkItems wi
WHERE  WorkItemId = @WorkItemId
  AND  wi.IsComplete = 0;

It looks like, since the result would be 0 or 1 only, guessing the purpose and based on seeking how to write Any(), it may be written as:

SELECT  CASE WHEN EXISTS ( SELECT *
                           FROM   Warehouse.WorkItems wi
                           WHERE  WorkItemId = @WorkItemId AND
                                  wi.IsComplete = 0 ) THEN 1
             ELSE 0
        END;

Then it makes sense to use Any():

bool exists = db.WorkItems.Any( wi => wi.WorkItemId == workItemId & !wi.IsComplete );

EDIT: I misread the original query in a hurry, sorry. Here is an update on the Linq usage:

bool exists = db.WorkItems.Any( wi => 
   db.WorkItems
    .SingleOrDefault(wi.WorkItemId == workItemId).TaskId == wi.TaskId
  && !wi.IsComplete );

If the count was needed as in the original SQL:

var count = db.WorkItems.Count( wi => 
   db.WorkItems
    .SingleOrDefault(wi.WorkItemId == workItemId).TaskId == wi.TaskId
  && !wi.IsComplete );

Sorry again for the confusion.

Upvotes: 1

Ivan Stoev
Ivan Stoev

Reputation: 205799

The direct translation could be something like this

var result = db.WorkItems.Any(wi =>
    !wi.IsComplete && wi.TaskId == db.WorkItems
        .Where(x => x.WorkItemId == workItemId)
        .Select(x => x.TaskId)
        .FirstOrDefault()));

Taking into account the fact that SQL =(subquery), IN (subquery) and EXISTS(subquery) in nowadays modern databases are handled identically, you can try this instead

var result = db.WorkItems.Any(wi =>
    !wi.IsComplete && db.WorkItems.Any(x => x.WorkItemId == workItemId
        && x.TaskId == wi.TaskId));

Upvotes: 3

Cameron
Cameron

Reputation: 2594

Turns out that I just needed to approach the problem from a different angle.

I came up with about three solutions with varying Linq syntaxes:

Full method chain:

var q1 = Warehouse.WorkItems
                  .Where(workItem => workItem.TaskId == (from wis in Warehouse.WorkItems
                                                           where wis.WorkItemId == workItemId
                                                           select wis.TaskId).First())
                  .Any(workItem => !workItem.IsComplete);

Mixed query + method chain:

var q2 = Warehouse.WorkItems
                  .Where(workItem => workItem.TaskId == Warehouse.WorkItems
                                                                 .Where(wis => wis.WorkItemId == workItemId)
                                                                 .Select(wis => wis.TaskId)
                                                                 .First())
                  .Any(workItem => !workItem.IsComplete);

Full query:

var q3 = (from wi in Warehouse.WorkItems
          where wi.TaskId == (from swi in Warehouse.WorkItems
                              where swi.WorkItemId == workItemId
                              select swi.TaskId).First()
          where !wi.IsComplete
          select 1).Any();

The only problems with this is that it comes up with some really jacked up SQL:

SELECT 
    (CASE 
        WHEN EXISTS(
            SELECT NULL AS [EMPTY]
            FROM [Warehouse].[WorkItems] AS [t0]
            WHERE (NOT ([t0].[IsComplete] = 1)) AND ([t0].[TaskId] = ((
                SELECT TOP (1) [t1].[TaskId]
                FROM [Warehouse].[WorkItems] AS [t1]
                WHERE [t1].[WorkItemId] = @p0
                )))
            ) THEN 1
        ELSE 0
     END) AS [value]

Upvotes: 2

Aaron Yarborough
Aaron Yarborough

Reputation: 136

You can use the Any() function like so:

var result = Warehouse.WorkItems.Any(x => x.WorkItemId != null);

In short, you pass in your condition, which in this case is checking whether or not any of the items in your collection have an ID

The variable result will tell you whether or not all items in your collection have ID's.

Here's a helpful webpage to help you get started with LINQ: http://www.dotnetperls.com/linq

Upvotes: 1

Related Questions