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

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();

Related

How to use Linq SQL to perform INNER JOINs where values are equal or one is null

How can I perform the following query using Linq
SELECT u.* FROM Users u
INNER JOIN Localities l ON (u.State = l.State or l.State is NULL)
AND (u.City = l.City or l.City is NULL)
You can try out the following query. Also to make it more optimize you can remove your null conditions as inner join will only work if both of the records contains value
var users = from u in Users
join l in Localities on new { A = u.State, B = u.City}
equals new { A = l.State, B = l.City }
select u
l.State is NULL and l.city is null will eliminated by default from inner joins so you don't need to make joins on null
Try the following, you may need to change a bit as it is not tested.
var result = from Users u
join Localities l
on new { u.state, u.City } equals new { l.state, l.City }
where (l.state == NULL || l.City == NULL)
select u;

Convert SQL WHERE with COALESCE into LINQ

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)

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 SQL Join

I am learning Linq2SQL and I have a question on the left outer join.
In my example below I believe that I am performing the left outer join on the questions table to the favoritequestions table. However I don't believe that my where clause is correct.
So if I perform a left out join on two tables how should I set up the where clause appropriately?
var myResults = from quest in context.MyQuestions
join favQuest in context.MyFavoriteQuestions on quest.UserFavoriteQuestionId equals favQuest.UserFavoriteQuestionId
join specialQuest in context.Questions on favQuest.QuestionId equals specialQuest.QuestionId into joinedQuestions
from specialQuest in joinedQuestions.DefaultIfEmpty()
where (quest.UserId == userId) &&
( specialQuest.Id == paramId && (!specialQuest.IsBlue || (specialQuest.IsBlue && canViewBlueQuestion)) &&
(!specialQuest.IsRed || (specialQuest.IsRed && canViewRedQuestion))
)
select quest;
For LINQ to SQL contexts it is suggested to write the left outer join as such, as that actually generates a SQL LEFT JOIN:
var myResults = from question in context.MyQuestions
from favoriteQuestion in context.MyFavoriteQuestions
.Where(fc => fc.UserFavoriteQuestionId == question.UserFavoriteQuestionId)
.DefaultIfEmpty()
It is also suggested (to improve legibility) to separate unrelated (and ANDed) where clauses:
var myResults = from question in context.MyQuestions
where question.UserId == userId
from favoriteQuestion in context.MyFavoriteQuestions
.Where(fc => fc.UserFavoriteQuestionId == question.UserFavoriteQuestionId)
.DefaultIfEmpty()
from specialQuestion in context.Questions
.Where(sc => sc.QuestionId == favoriteQuestion.QuestionId)
.DefaultIfEmpty()
where specialQuestion.Id == paramId
where !specialQuestion.IsBlue || (specialQuestion.IsBlue && canViewBlueQuestion)
where !specialQuestion.IsRed || (specialQuestion.IsRed && canViewRedQuestion)
select question;

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)

Categories