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
))
Related
I'm trying to produce a filtered list in a controller to pass to a MVC view.
I know I can do this in SQL: (I've shortened the SQL somewhat...)
SELECT ItemID, Item, SupplierID, Supplier, BrandID, Brand, CategoryID, Category, SubCategoryID, SubCategory, TypeID, Type
FROM Table1 join Table 2... etc
WHERE (supplierid = #SID OR #SID = -99) AND
(brandid = #BID OR #BID = -99) AND
(categoryid = #CID OR #CID = -99) AND ....
but I would like to know the best way to do it in C# & Linq (or if it's just better to call a stored proc based on the above and use that instead)
Here is the start of my controller:
public async Task<ActionResult> Index(int? supplierid, int? itembrandid, int? categoryid, int? subcategoryid, int? itemtypeid)
{
List<DTOClasses.ItemsListDTO> items = new List<DTOClasses.ItemsListDTO>();
items = await (from i in db.Items
join b in db.ItemBrand on i.BrandID equals b.BrandID
join s in db.Suppliers on b.SupplierID equals s.SupplierID
join sc in db.SubCategories on i.SubCategoryID equals sc.SubCategoryID
join c in db.Categories on sc.CategoryID equals c.CategoryID
where // get stuck at this point
select new DTOClasses.ItemsListDTO
{
ItemId = i.ItemID
//etc..
}
).ToListAsync();
return View(items);
}
I wanted to avoid having to write potentially 25 nested if statements if possible.
** Edit
for example - to keep the linq query neat and tidy, have each combination of input params with their own query to execute.
if( BID != null && SID != null && CID != null)
{ query 1}
else
if (BID != null && SID != null && CID == null)
{query 2}
C# has the equivalent of COALESCE using the null coalescing operator ??, but I am not sure how that is relevant to your question. C# also has the exact equivalent of multiple conditions with AND and OR in a where, so just like SQL you can do:
items = await (from i in db.Items
join b in db.ItemBrand on i.BrandID equals b.BrandID
join s in db.Suppliers on b.SupplierID equals s.SupplierID
join sc in db.SubCategories on i.SubCategoryID equals sc.SubCategoryID
join c in db.Categories on sc.CategoryID equals c.CategoryID
where ((b.BrandID == BID || BID == -99) &&
(s.SupplierID == SID || SID == -99) &&
(sc.CategoryID == CID || CID == -99))
select new DTOClasses.ItemsListDTO
{
ItemId = i.ItemID
//etc..
}
).ToListAsync();
If the repetitiveness bothers you as much as it does me, you could hide it in a helper extension method, but that assumes the magic number stays the same -99 for all the ID types (and ignores how bad using a magic number actually is).
static class IDExt {
static bool IDMatches(this int anID, int testID) => testID == anID || testID == -99;
}
then you have
where b.BrandID.IDMatches(BID) && s.SupplierID.IDMatches(SID) && sc.CategoryID.IDMatches(SID)
I am trying to create a where clause in Linq to SQL using the following logic
if #supplierid is null return all records.
if #supplierid is not null return where supplierid is equals to #supplierid.
and the one that is creating an issue:
if #supplierid ==0 return all records where supplierid is null
I tried writing this like
var answers =
from thisChargeableService in this.GetAll()
where
(
(
(supplierId == null) ||
(
((supplierId < 1) && (thisChargeableService.SupplierId == null)) ||
((supplierId != null) && (thisChargeableService.SupplierId == supplierId.Value))
)
));
This works with the first two conditions but when #supplierid = 0, nothing is returned.
Any help with this would be much appreciated
edit
Basically I have a dropdown of N/A with an id of 0. I have used this to identify that an option has been selected from dropdown and the user is targeting all rows where the supplier id is N/A.
The database contains no entries with 0 as the supplierid, so instead I am trying to target this with where the supplierid is null or the below in SQL
SELECT * FROM ChargeableService
WHERE
(#supplierid is null)
OR
(
(#supplierid is not null and supplierid = #supplierid) or
(#supplierid = 0 AND supplierid is null)
)
With Linq, there is no need to try to build one query to do all. Instead you can build your expression in buts and let deferred execution build and execute the correct sql.
So, this is the way I would do it.
var answers = this.GetAll().AsQueryable();
if (supplierId.HasValue && (supplierId.Value != 0))
answers = answers.Where(a=>a.SupplierId == supplierId.Value);
if (supplierId.HasValue && (supplierId.Value == 0))
answers = answers.Where(a=>!a.SupplierId.HasValue);
I've taken your query and run it against some similar data and the following works:
var answers =
from thisChargeableService in this.GetAll()
where
(
supplierId == null ||
(supplierId == 0 && thisChargeableService.SupplierId == null) ||
(supplierId > 0 && thisChargeableService.SupplierId == supplierId)
)
select thisChargeableService;
I've been trying to modify some rows of data in SQL to test in my application and I've noticed my query in Lambda brings back 0 rows when I am expecting 2387 row. The source of the problem is I am using parenthesis in a WHERE clause in SQL to look at some null values. This is the SQL query:
SQL
-- THIS WORKS!
select * from vwAppsWithIssues
where fld1stCheckAllocatedTo = 'nicholasg' and fldStage = 1
and (fldStopStartDate is null or fldStopEndDate is not null)
-- The query was originally this (doesn't return rows)
select * from vwAppsWithIssues
where fld1stCheckAllocatedTo = 'nicholasg' and fldStage = 1
and (fldStopStartDate = null or fldStopEndDate <> null)
LAMBDA query that returns 0 rows
public static int GetApplicationsFirstCount(string UserId)
{
try
{
using (IME_CheckOffEntities IME_CheckOffEntities = new IME_CheckOffEntities())
{
return IME_CheckOffEntities.vwAppsWithIssues
.Where(a => a.fld1stCheckAllocatedTo == UserId && a.fldStage == 1 && (a.fldStopStartDate == null || a.fldStopEndDate != null))
.ToList().Count;
}
}
catch (Exception ex)
{
throw;
}
}
Update
Using LINQPad I have written this expression:
VwAppsWithIssues
.Where (v => v.Fld1stCheckAllocatedTo == "nicholasg"
&& v.FldStage == 1
&& (v.FldStopStartDate == null || v.FldStopEndDate != null)).Count()
that generates this sql
SELECT COUNT(*) AS [value]
FROM [vwAppsWithIssues] AS [t0]
WHERE ([t0].[fld1stCheckAllocatedTo] = #p0) AND ([t0].[fldStage] = #p1) AND (([t0].[fldStopStartDate] IS NULL) OR ([t0].[fldStopEndDate] IS NOT NULL))
So now that I have some lambda that I think will work, I simply copy it to visual studio.
var count = IME_CheckOffEntities.vwAppsWithIssues
.Where(v => v.fld1stCheckAllocatedTo == "nicholasg" && v.fldStage == 1 && (v.fldStopStartDate == null || v.fldStopEndDate != null)).Count();
It still returns only 0 rows?! I am passing in the right userId in C# as well.
My count in c# also returns 0 rows. Any idea how I can rewrite this C# query?
from linqpad, on one of my schema
from
f in Files
where
f.PubDate == null || f.FilingDate != null
select
f.IdFile
is translated as follow
SELECT
[Extent1].[idFichier] AS [idFichier]
FROM [dbo].[tableF] AS [Extent1]
WHERE ([Extent1].[datePubliF] IS NULL) OR ([Extent1].[dateDepotF] IS NOT NULL)
so, in your case, are you, for example, sure of the UserId value ?
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
};
I have database
Table room: id,name
Table table: id, id_room
Table WorkPanel: id, id_table, **date**.
I use (date == date, and WorkPanel have record with table):
var nowWorkPanels = from a in context.WorkPanels where a.date == date select a;
but I do not understand how to make a check at that time and in this room there is a record
You are obviously not showing all of the fields in your tables, but if your navigational properties are set up correctly, your query will look something like:
from wp in context.WorkPanels
where wp.date == date && wp.Table.id_room == roomId
select wp;
or if you want to query by room name:
from wp in context.WorkPanels
where wp.date == date && wp.Table.Room.name == roomName
select wp;
I prefer the more concise method chaining syntax, though:
context.WorkPanels.Where(wp => wp.date == date && wp.Table.id_room == roomId);