Convert SQL WHERE with COALESCE into LINQ - c#

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)

Related

C# LINQ select from table and join multiple table and join a SQL query view in SQL server

I have a code that selecting from table and joining multiple tables and joining dbContext.Database.sqlQuery from view in sql server.
But it gives me this error
Unable to create a constant value of type
'ITManagement.Models.Employee'. Only primitive types or enumeration
types are supported in this context.
My code
public JsonResult getEmployeeAsset(EmployeeController employee)
{
var employeeID = Request.QueryString["employeeID"];
var devices = (from asset in db.Devices
where asset.EmployeeID == employeeID
join brand in db.DeviceBrands on asset.Brand equals brand.ID
join model in db.DeviceModels on asset.Model equals model.ID
join type in db.DeviceTypes on asset.DeviceType equals type.ID
join room in db.Rooms on asset.FullRoomCode equals room.FullCode
//if device has last employee
join lsEmp in db.Database.SqlQuery<LDAPUsers>("SELECT * FROM V_LDAP_Users") on asset.LastEmployeeID equals lsEmp.employeeID into lstEmp
join sysUser in db.AspNetUsers on asset.sscUser equals sysUser.Id
from lastEmployee in lstEmp.DefaultIfEmpty()
select new
{
deviceID = asset.ID,
SerialNumber = asset.SerialNumber,
Type = type.Type,
BrandName = brand.BrandName,
ModelName = model.ModelName,
MaccCode = asset.MaccCode,
PONumber = asset.PONumber,
WarrantyDate = asset.WarrantyDate.ToString(),
MacAddress = asset.MacAddress,
WIFIMacAddress = asset.WIFIMacAddress,
PCName = asset.PCName,
LastEmployee = asset.LastEmployeeID + "-" + lastEmployee.employeeName,
Shared = asset.Shared == 1 ? "True" : "False",
Location = room.RoomName,
RecordedBy = sysUser.Name,
requestID = (from request in db.StoreRequests where request.DeviceID == asset.ID && request.State == 1 && request.VoucherType == "ASD" orderby request.ID select request.ID).FirstOrDefault()
}).DefaultIfEmpty();
return Json(new { assets = devices == null ? null : devices }, JsonRequestBehavior.AllowGet);
}
Your help please, thanks.
First of all, have you tried nested queries by commenting them out?
for example;
//join lsEmp in db.Database.SqlQuery<LDAPUsers>("SELECT * FROM V_LDAP_Users") on asset.LastEmployeeID equals lsEmp.employeeID into lstEmp
//requestID = (from request in db.StoreRequests where request.DeviceID == asset.ID && request.State == 1 && request.VoucherType == "ASD" orderby request.ID select request.ID).FirstOrDefault()
If there is no problem in these two, you can quickly find out which one is causing the problem by commenting the fields.
Tip: Also, more than 3 joins will affect your query performance. Try to split your queries as much as possible.

LINQ - checking for null values then appending statement back to query

Am using Linq-to-SQL and have wrote the following query but I need to be able to check that the variable "UserRole.RoleID" is not null.
var MemberQuery = from SystemUser in SecurityFilter.FilteredMembers(SecurityInfo, Context, DetailLevelEnum.NameOnly)
join Member in Context.webpages_Memberships on SystemUser.ID equals Member.UserId
join UserRole in Context.webpages_UsersInRoles on Member.UserId equals UserRole.UserId
where Member.IsConfirmed || IncludeUnconfirmed && Filter.SelectedMemberRoles.Contains(UserRole.RoleId) // This will sometimes be null
select SystemUser;
I have thought through a number of approaches such as wrapping the query in an if / else statement or creating an anonymous type. Am not sure what the best approach would but I am trying to do something like this:
var MemberQuery = from SystemUser in SecurityFilter.FilteredMembers(SecurityInfo, Context, DetailLevelEnum.NameOnly)
join Member in Context.webpages_Memberships on SystemUser.ID equals Member.UserId
where Member.IsConfirmed || IncludeUnconfirmed
select SystemUser;
if (Filter.SelectedMemberRoles != null)
{
MemberQuery = MemberQuery.Where( // Somthing here
join UserRole in Context.webpages_UsersInRoles on Member.UserId equals UserRole.UserId
where Filter.SelectedMemberRoles.Contains(UserRole.RoleId)
select /* Somthing */).Any();
}
How can I append the second part of the query wrapped within the if condition to the original query?
You could try this:
var MemberQuery = (from SystemUser
in SecurityFilter.FilteredMembers(SecurityInfo, Context, DetailLevelEnum.NameOnly)
join Member in Context.webpages_Memberships on SystemUser.ID
equals Member.UserId
join _UserRole in Context.webpages_UsersInRoles on Member.UserId
equals _UserRole.UserId into _UserRole
from UserRole in _UserRole.DefaultIfEmpty()
where (Member.IsConfirmed || IncludeUnconfirmed )
&& (
Filter.SelectedMemberRoles == null || UserRole != null
&& Filter.SelectedMemberRoles.Contains(UserRole.RoleId)
)
select SystemUser)
.Distinct();

Join with Where Clause in LINQ

I've got two tables that I'm trying to join together with an ID, but only select rows from Table A, where a value in Table B is null.
I tried this:
var dbXMLSoccerTeams = (from dbXMLSoccerTeam in data.EFXMLSoccerTeam
where dbXMLSoccerTeam.ID == (from dbMatchTeam in data.EFMatchingTeamIDs
where dbMatchTeam.SmarketsID == null
select dbMatchTeam.XMLSoccerID)
select dbXMLSoccerTeam);
But I get an error saying that operator == can not be used to compare int to iQueryable int
It seems to me that you should actually use a join:
var dbXMLSoccerTeams = from dbXMLSoccerTeam in data.EFXMLSoccerTeam
join dbMatchTeam in data.EFMatchingTeamIDs
on dbXMLSoccerTeam.ID equals dbMatchTeam.XMLSoccerID
where dbMatchTeam.SmarketsID == null
select dbXMLSoccerTeam;
Try:
var dbXMLSoccerTeams = (from dbXMLSoccerTeam in data.EFXMLSoccerTeam
from dbMatchTeam in data.EFMatchingTeamIDs
where dbMatchTeam.SmarketsID == null
&& dbXMLSoccerTeam.ID == dbMatchTeam.XMLSoccerID
select dbXMLSoccerTeam)

LINQ to Entities Join on Nullable Field where Null Implies "Match All"

I am attempting to run the following LINQ query using Entity Framework 5:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on a.State equals (c.State ?? a.State)
where 2 == c.Id_Task
&& b.DataType == c.DataType
select a.Id;
The line that is causing me problems is:
on a.State equals (c.State ?? a.State)
The "State" field in Table_C is nullable... and when it is null, it is used to imply "all states". As such, when "c.State" is null I want the record to be matched. If I were to write this in SQL, I would use the following:
JOIN Table_C ON Table_A.State = ISNULL(Table_C.State, Table_A.State)
Unfortunately, I am being given the following error:
The name 'a' is not in scope on the right side of 'equals'. Consider swapping the expressions on either side of 'equals'.
I will be grateful to anybody who can let me in on the secret to getting this working.
Thanks.
You can modify your code like:
int taskId = 2;
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
from c in Table_C
where 2 == c.Id_Task
&& b.DataType == c.DataType
&& (c.State == null || a.State.Equals(c.State))
select a.Id;
A join is essentially a where clause, so here we can use the where clause due to the restrictions with join.
I managed to get this to work by moving the "DataType" check from the WHERE to the JOIN, and moving the "State" check from the JOIN to the WHERE. The resulting code that worked as I expected is as follows:
query = from a in Table_A
where a.StatusCode != "DONE"
&& a.Inbound
join b in Table_B
on a.Id equals b.Id_Table_A
join c in Table_C
on b.DataType equals c.DataType
where 2 == c.Id_Task
&& (c.State ?? a.State) == a.State
select a.Id;
Many thanks to everybody who has taken a look at this for me. :)
You can use "from" syntax instead of "join"
from a in TableA
from b in TableB
.Where(x => (x.Buy ?? a.Buy) == a.Buy
&& (x.Parity ?? a.Parity) == a.Parity)

linq query help

I have a search method below using linq, my search citeria is based on one table so far but I have a particular citeria which requires another table :
Method:
public void search(string s)
{
var db = new CommerceEntities();
var products =
from p in db.Products
where (p.ModelName != null && p.ModelName.Contains(s))
|| SqlFunctions.StringConvert((double) p.ProductID).Contains(s)
|| (p.ModelNumber != null && p.ModelNumber.Contains(s))
|| (p.Description != null && p.Description.Contains(s))
|| SqlFunctions.StringConvert((double) p.CategoryID).Contains(s)
|| //stuck - See comment below
/* so far all in 'products' table, but I have one more citeria here that needs 'categories' table.
Ok, the query in SQL statement will be like this:
select ProductID, ModelName, ProductImage, Unitcost, products.Categoryid
from categories
join products
on (categories.CategoryID = products.categoryID)
where categories.categoryname = 'necklace' (replace necklace with my parameter 's')
order by products.ModelName
I am not sure how to 'integrate' it with my existing linq query. Please kindly advice. Thanks.
*/
select new
{
// Display the items
p.ProductID,
p.ModelName,
p.ProductImage,
p.UnitCost,
p.CategoryID,
};
ListView_Products.DataSourceID = null;
ListView_Products.DataSource = products;
}
var products = from p in db.Products
join c in categories on c.CategoryID equals p.categoryID
where (/* all you conditions in either tables */)
select new
{
p.ProductID,
p.ModelName,
p.ProductImage,
p.UnitCost,
p.CategoryID
/* plus what ever you want from categories */
};

Categories