Linq To SQL Join - c#

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;

Related

How to join with or condition? Cross Join is creating a IS NULL where clause

Basically I want to create an inner or join. In SQL:
SELECT COUNT(*) FROM Discounts
JOIN DiscountArticles ON Discounts.ID = DiscountArticles.Discount
JOIN Articles ON (DiscountArticles.Article = Articles.ID OR DiscountArticles.ArticleGroup = Articles.ArticleGroup)
This answer says, it's not possible using entity framework, however we can use an cross join, which works fine on SQL:
SELECT COUNT(*) AS GroupCount FROM Discounts
JOIN DiscountArticles ON Discounts.ID = DiscountArticles.Discount
CROSS JOIN Articles
WHERE (Articles.ID = DiscountArticles.Article OR Articles.ArticleGroup = DiscountArticles.ArticleGroup)
So I tried this:
var query = from discounts in _dbContext.Discounts
join discountArticles in _dbContext.DiscountArticles on discounts.Id equals discountArticles.Discount
from article in _dbContext.Articles
where article.Id == discountArticles.Article || article.ArticleGroup.Value == discountArticles.ArticleGroup.Value
select new
{
ArticleId = article.Id,
DiscountId = discounts.Id
};
However, this resolves into this this SQL query:
SELECT [a].[ID] AS [ArticleId], [d].[ID] AS [DiscountId]
FROM [Discounts] AS [d]
INNER JOIN [DiscountArticles] AS [d0] ON [d].[ID] = [d0].[Discount]
CROSS JOIN [Articles] AS [a]
WHERE ([a].[ID] = [d0].[Article]) OR (([a].[ArticleGroup] = [d0].[ArticleGroup]) OR (([a].[ArticleGroup] IS NULL) AND ([d0].[ArticleGroup] IS NULL)))
As you can see, there is an addtional check OR (([a].[ArticleGroup] IS NULL) AND ([d0].[ArticleGroup] IS NULL)), which is causing to return 6 time more results.
ArticleGroup is Nullable Guid? on both entities, so I guess it has something to do with it.
If I additional check it for null, I get the correct results:
where article.Id == discountArticles.Article ||
article.ArticleGroup.HasValue && article.ArticleGroup == discountArticles.ArticleGroup
However, I also get an bigger where clause in SQL:
WHERE ([a].[ID] = [d0].[Article]) OR (([a].[ArticleGroup] IS NOT NULL) AND ([a].[ArticleGroup] = [d0].[ArticleGroup]))
Is it somehow possible to generate a Query, which is more like my second SQL example? Something like this:
WHERE (Articles.ID = DiscountArticles.Article OR Articles.ArticleGroup = DiscountArticles.ArticleGroup)
Try this query, it will create appropriate join. I do not think that you need CROSS JOIN here.
var query =
from discounts in _dbContext.Discounts
join discountArticles in _dbContext.DiscountArticles on discounts.Id equals discountArticles.Discount
from article in _dbContext.Articles
.Where(article => article.Id == discountArticles.Article || article.ArticleGroup.Value == discountArticles.ArticleGroup.Value)
select new
{
ArticleId = article.Id,
DiscountId = discounts.Id
};
According to null comparison, check this option Using relational null semantics. Bad here that it will affect all queries:
services.AddDbContext<MyDbContext>(options =>
{
options.UseSqlServer(sourceConnection, sqlOptions =>
{
sqlOptions.UseRelationalNulls();
});
});

How to use LINQ query using OR condition in join clause [duplicate]

I want to do a JOIN with LINQ using an OR statement.
Here is the SQL query I'm starting with:
SELECT t.id
FROM Teams t
INNER JOIN Games g
ON (g.homeTeamId = t.id OR g.awayTeamId = t.id)
AND g.winningTeamId != 0
AND g.year = #year
GROUP BY t.id
I'm having trouble converting that ON clause to LINQ. This is where I'm at:
var y = from t in db.Teams
join g in db.Games on t.ID equals g.AwayTeamID //missing HomeTeamID join
where g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
I think I could use:
join g in db.Games on 1 equals 1
where (t.ID == g.HomeTeamID || t.ID == g.AwayTeamID)
and this works but seems kind of seems hacky. Is there a better way?
I struggled with this as well until I found the following solution, which worked well for my situation:
var y = from t in db.Teams
from g in db.Games
where
(
t.ID == g.AwayTeamID
|| t.ID == g.HomeTeamID
)
&& g.WinningTeamID != 0
&& g.Year == year
group t by t.ID into grouping
select grouping;
Under the covers, your solution probably works very close to this one. However, I bet this one is just a bit faster if you benchmark it since it is not JOINING every item in the first dataset with every item in the second dataset, which could be a disaster if either (or both) dataset were really big.
The where clause applies a boolean condition, so using "||" is the way to go. You can chain multiple where clauses but I believe that will give you a "and" operation, rather than an "or".
I think you can do like this:
from t1 in db.Table1
// inner join with OR condition
from t2 in db.Table2 where t1.col1 == t2.col1 || t1.col2 == t2.col2
// normal inner join
join t3 in db.Table3 on t1.col1 equals t3.col1
// inner join with complex condition
join t4 in db.Table4 on t2.col4 equals t4.col4 where t2.col5.Contains(t4.col5)
// left join with OR condition
from t5 in db.Table5.Where(x => x.col5 == t1.col5 || x.col6 == t1.col6).DefaultIfEmpty()
select new {
x = 1 // select whatever you want here
}
The underlying SQL query probably won't use native sql joins but the above is just a way to make your code look pretty and organized.

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

SQL Query to LINQ for MVC with SUM and IS NOT NULL

Can someone please help me convert this query to LINQ as I am new to using Linq which will then be used for google chart information.
Select Question.SubSectionName, SUM(Answers.RatingAnswer) AS Ratings
FROM Question,Answers,Response,Section
Where Answers.QuestionID = Question.QuestionID
AND Answers.ResponseID = Response.ResponseID
AND Question.SectionID=Section.SectionID
AND Section.SectionID = 2
AND Response.ResponseID = #0
AND Question.SubSectionName IS NOT Null
GROUP BY Question.SubSectionName;
What I've got so far :
var submitted = (from ans in db.Answers join ques in db.Questions on
ans.QuestionID equals ques.QuestionID
join resp in db.Responses on ans.ResponseID equals resp.ResponseID
join sec in db.Sections on ques.SectionID equals sec.SectionID
where sec.SectionID == 2 && resp.ResponseID == model.ResponseID
&& ques.SubSectionName!= null
select ques.SubSectionName && ans.RatingAnswer)
Thanks for any help.
Building on your comment this should output a grouping of sums of RatingAnswers:
var submitted =
(from ans in db.Answers
join ques in db.Questions on ans.QuestionId equals ques.QuestionId
join resp in db.Responses on ans.ResponseId equals resp.ResponseId
join sec in db.Sections on ques.SectionId equals sec.SectionId
where sec.SectionId == 2 && resp.ResponseId == model.ResponseID && ques.SubSectionName != null
select new { SubSectionName = ques.SubSectionName, RatingAnswer = ans.RatingAnswer })
.GroupBy(a => a.SubSectionName)
.Select(a => new { SectionName = a.Key, Sum = a.Sum(s => s.RatingAnswer) });
There may be a more efficient way of writing this.
I would also point out that to me the data structure seems flawed. That is, it seems either normalized incompletely or improperly. That's certainly for you to work out on your own.
Linqer helps you to convert SQL to LINQ.
If you want to get better in LINQ, I recommend using LINQPad. However, LINQPad can't convert from SQL to LINQ but from LINQ to SQL.
try this-
from q in Question
join a in Answers on q.QuestionID equals a.QuestionID
join r in Response on r.ResponseID equals a.ResponseID
join s in Section on s.SectionID equals q.SectionID
where s.SectionID= 2 and r.ResponseID= #0 and q.SubSectionName!=null
Group by q.SubSectionName

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