Conditional linq query - c#

What would be the best approach for converting this SQL to linq?
I've earlier made views in the database based on such an sql, and then query the view with linq. But I've would like to know if there are other approaches.
The SQL finds the assigned object for an task. The task table contains three foreign key columns, as the assigned to may be from Department, Position or a Person. Only one is allowed.
SQL:
SELECT id,
title,
assigned_to = (case
when idAssignedDepartment is not null then (select description from department where id = idAssignedDepartment)
when idAssignedPosition is not null then (select description from position where id = idAssignedPosition )
when idAssignedPerson is not null then (select fullname from person where id = idAssignedPerson )
end)
FROM task
Using LinqToEF

You can write it like this:
var q = from t in task
from dep in department.Where(x => x.id == t.idAssignedDepartment).DefaultIfEmpty()
from pos in position.Where(x => x.id == t.idAssignedPosition).DefaultIfEmpty()
from per in person .Where(x => x.id == t.idAssignedPerson).DefaultIfEmpty()
select new
{
t.id,
t.title,
assigned_to = t.idAssignedDepartment != null ? dep.description :
t.idAssignedPosition != null ? pos.description :
t.idAssignedPerson != null ? per.fullname :
null
};

Related

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
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));
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]
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
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.

Linq query to SQL query

I have a Linq Query that works well but I need to write the SQL Query
Can Anybody help me write it?
this query will search the database foreach a.h and a.HV in the view with the filters of time and model and in the end it checks the option Filter.M that if it is selected it will search for all the data selected in this DropDownCheckBoxes`
How can i write the this where and select part in SQL command?
ret1 = (from a in View
where
a.LastRefreshTime>=Filter.From && a.LastRefreshTime<=Filter.To && a.ModelCode == mdlCode &&
Filter.PN.Select(epn => epn.Substring(0, 11)).Contains(a.H) &&
Filter.PN.Select(epn => epn.Substring(14, 2)).Contains(a.HV)
select new RData
{
v = a.v,
Date = a.LastRefreshTime,
UserId = a.UserId,
M = a.Name,
}).Distinct().AsQueryable();
ret = ret1.Where(nr =>
Filter.M == null || !Filter.M.Any() || Filter.M.Contains(nr.M)
).ToList();
Here's a start for you
select a.v v,
a.LastRefreshTime "Date",
a.UserId,
a.Name
from a
where a.LastRefreshTime>= arg_filter_from
and a.LastRefreshTime<= arg_filter_to
and a.ModelCode = arg_mdlCode
.
.
.
In this query you'll need to replace 'arg_...' with the appropriate values or arguments you want.
Contains is roughly equivalent to "IN" in SQL. For example:
where a.Name in ('jim', 'bob', 'joe')
In can also be used with a subselect which is roughly what I think Filter.PN.Select is doing though I'm not a linq expert. Example:
where a.H in (Select foo from PN_Table)
Or simpler example continuing on the my previous name example:
where a.Name in (select first_name from table)
If we supposed that the Filter.PN list represent a table FilterPN in your sql database, that will be your converted code for the first linq query
select distinct a.v, a.LastRefreshTime, a.UserId, a.Name
from [view] a
where a.LastRefreshTime>= 'Filter.From' and
a.LastRefreshTime<='Filter.To' and a.ModelCode = 'mdlCode' and
exists(select top 1 * from FilterPN where Substring(epn, 1, 11) = a.H) and
exists(select top 1 * from FilterPN where Substring(eenter code herepn, 15, 2) = a.HV)
think to replace the enquoted variables with ur real values 'Filter.To'...

How to select and exclude records from the same subset in LINQ

I have a sample connection table PolicyToX with fields Id, PolicyId, PersonId, SchoolId. Records are always saved with one of the FKs being NULL, for example 1, 1, 5, NULL.
I want to write a query in LINQ that, when given two parameters: PersonId and SchoolId will filter all Policies of the given School but without those that are already bound to a given Person.
So, if I have a dataset of:
[Id][PolicyId][PersonId][SchoolId]
1 1 5 NULL
2 1 NULL 1
3 2 NULL 1
and pass paremeters PersonId = 5 and SchoolId = 1 the result should be one Policy of ID = 2.
Thanks!
Assuming PolicyToX contains the data then is this what you're looking for?
var ids = from e in PolicyToX where e.PersonId == personId select e.PolicyId;
var result = from d in PolicyToX where d.SchoolId == schoolId && !ids.Contains(d.PolicyId) select d;
var data = list.Where(x => x.SchoolId == schoolId && x.PersonId != personId);
Are you talking about this?

Nullable Guid in Linq to Sql Yields Unexpected Result

I have a simple SQL table which defines a set of hierarchical categories and sub-categories - note the ParentCategoryId can be null for 'top-level' categories...
CREATE TABLE [dbo].[Category](
[CategoryId] [uniqueidentifier] NOT NULL,
[ParentCategoryId] [uniqueidentifier] NULL,
[Name] [nvarchar](50) NOT NULL
)
If I then construct a Linq expression to find a particular category by Name and ParentCategoryId, I find that I cannot get the correct result if I set a Guid? variable to null:
Guid? parentCategoryId = null;
var category = dc.Categories
.Where(c => (
(c.Name == "Fred") &&
(c.ParentCategoryId == parentCategoryId)
));
This does not yield the same result as:
var category = dc.Categories
.Where(c => (
(c.Name == "Fred") &&
(c.ParentCategoryId == null)
));
From what I can find on the web, others have had this problem, but I haven't been able to find a clean workaround to fix the problem.
Any ideas would be much appreciated. Thanks.
Additional Information
Here is the LINQ generated SQL statements for firstly a Guid? null parameter and then for a simple null parameter:
-- With Guid? null parameter : return an empty record set
DECLARE #p0 NVarChar(1000) SET #p0 = 'Fred'
DECLARE #p1 UniqueIdentifier SET #p1 = null
SELECT [t0].[CategoryId], [t0].[ParentCategoryId], [t0].[Name], [t0].[Timestamp]
FROM [dbo].[cad_ScoCategory] AS [t0]
WHERE ([t0].[Name] = #p0) AND ([t0].[ParentCategoryId] = #p1)
-- With null parameter - returns a single (correct) record
DECLARE #p0 NVarChar(1000) SET #p0 = 'Fred'
SELECT [t0].[CategoryId], [t0].[ParentCategoryId], [t0].[Name], [t0].[Timestamp]
FROM [dbo].[cad_ScoCategory] AS [t0]
WHERE ([t0].[Name] = #p0) AND ([t0].[ParentCategoryId] IS NULL)
As you can see, the first option compares the ParentCategoryId with a nulled paraemeter where as the second method checks ParentCategoryId IS NULL - which is correct
No, this is a reasonably common problem unfortunately. The workaround is to explicitly compare with null:
.Where(c => c.Name == Fred &&
((c.ParentCategoryId == parentCategoryId) ||
(c.ParentCategoryId == null && parentCategoryId == null)))
Alternatively, perform the check outside the query (to change which filter is used):
var category = dc.Categories.Where(c => c.Name == "Fred");
category = parentCategoryId == null
? category.Where(c => c.ParentCategoryId == null)
: category.Where(c => c.ParentCategoryId == categoryId);

LINQ dropping results

I am trying to convert a complex (and rather hacky) dynamic SQL query in to a LINQ query.
I have the following LINQ query so far:
var results = (
from c in Customers
from d in MonthCalendar
join f in Facilities on c.CustomerCode equals f.CustomerCode
join p in ProjectedCashFlows on f.FacilityId equals p.FacilityId into pj
from p in pj.DefaultIfEmpty()
where d.ExpectedYear == currentYear
&& f.FacilityStatusId == 1
&& (p.ExpectedYear == null || d.ExpectedYear == p.ExpectedYear)
&& (p.ExpectedMonth == null || d.ExpectedMonth == p.ExpectedMonth)
&& c.PrimaryArmId == userId
&& (p.ProjectedCashFlowStatusId == null || p.ProjectedCashFlowStatusId != 4)
select new
{
CustomerCode = c.CustomerCode,
CustomerName = c.CustomerName,
FacilityId = f.FacilityId,
FacilityDescription = f.FacilityProductDescription,
FacilityCurrency = f.FacilityCurrencyId,
FacilityLimit = f.Limit,
ExpectedYear = d.ExpectedYear,
ExpectedMonth = d.ExpectedMonth,
ExpectedAmount = p == null ? 0 : (double)p.ExpectedAmount
}
);
I am trying to retrieve details from a Customer table that has a one-to-many relationship with a Facilities table. I am then trying to retrieve any details located in the ProjectedCashFlows
The problem I am having is that the query should return all Customer and Facilites information regardless of whether any values exist in the ProjectedCashFlows table.
Unfortunately this query is not doing that - it is only returning Customer and Facilities information when the Facility exists in the ProjectedCashFlows table.
I have used a MonthCalender table to list out each month in the year.
The relevant table information is:
Customers
CustomerCode
CustomerName
PrimaryArmId
Facilities
CustomerCode
FacilityId
FacilityCurrencyId
FaciliyLimit
FacilityDescription
ProjectedCashFlows
CustomerCode
FacilityId
ExpectedYear
ExpectedMonth
ExpectedAmount
ProjectedCashFlowStatusId
MonthsCalendar
ExpectedMonth
ExpectedYear
As an example I have a customer that has 4 rows in the Facilities table however, 2 of these facilities do not appear in the ProjectedCashFlows table so they are not being displayed.
If an entry doesn't exist in ProjectedCashFlows it should take the ExpectedMonth & ExpectedYear from the CalendarMonths table, return 0 for the ExpectedAmount and use the FacilityId from the Facilities table.
As you can probably work out I have just started to use LINQ.
Can anyone poke me in the right direction?
Your query uses p assuming it is non-null:
where d.ExpectedYear == currentYear
&& f.FacilityStatusId == 1
&& (p.ExpectedYear == null || d.ExpectedYear == p.ExpectedYear)
// etc
But you've used DefaultIfEmpty() which will logically create a sequence with a single null value when there are no ProjectedCashFlows.
So basically you need something like:
where d.ExpectedYear == currentYear
&& f.FacilityStatusId == 1
&& (p == null ||
((p.ExpectedYear == null || d.ExpectedYear == p.ExpectedYear)
// etc
))

Categories