Left/outer join with linq on c# with where condition clause - c#

I have 2 tables that I need to join in a query.
The first table is the Entries table which contain certain events such as Dance, Speak, Sing, Play, etc.
Id|Name
1|Dance
2|Sing
3|Speak
4|Play
5| etc.
The other table contains userEntries which indicates each user's score on each of the events
Id| UserId|EntryId|Score
1|898128 | 1 |200
2|827329 | 2 |120
3|898128 | 2 |100
Now I want a linq query to first of all get all the entries and then get the scores for a given user for the entries retunining null for the entry score whete the user has noscore
Example
for user 898128, I want to see something like this
Dance:200,Speak:null,Sing:120 from the result
I have tried the following linq query and I get an empty result
var userScores =
(from e in db.Entries join se in db.UserEntries
on e.Id equals se.EntryId
into ese from se in
ese.DefaultIfEmpty()
where se.UserId == "898128"
select new
{
EntryLabel=e.Label,
EntryValue=se.ValueAmount,
}).ToList();
ViewData["userScores "] = userScores;
I am running on ASP.NET core 2.0, entity framework core on a Windows 10 machine with Visual Studio 2017 15.6.3
I will appreciate any guide to getting the query right to give me an outer join so I can get all the entries for each user even where the user does not have any score.
Please note that this is different from this question errorneously marked by #Mahmoud as its duplicate. The difference lies in the presence of the WHERE condition clause.
Thank you

Try this query. it should fix your issue.
var userScores =(from e in db.Entries
join se in db.UserEntries on e.Id equals se.EntryId into ese
from nullse in ese.DefaultIfEmpty()
where (nullse==nulll ||(nullse!=null && nullse.UserId == "898128"))
select new
{
EntryLabel = e.Name,
EntryValue = nullse != null ? nullse.ValueAmount:"null"
}).ToList();

I have found the answer from this SO question. From there, I realized that the position of the where clause is the problem. See the working code revision below
var userScores =
(from e in db.Entries join se in db.UserEntries.Where(o => o.UserId ==
"898128"
on e.Id equals se.EntryId
into ese from se in
ese.DefaultIfEmpty()
select new
{
EntryLabel=e.Label,
EntryValue=se.ValueAmount,
}).ToList();
ViewData["userScores "] = userScores;
Thank you #Hazarath for your guide

Related

Linq to Sql multiple outer joins

Thanks in advance for any help.
Not an expert in Linq to Sql by any means.
I have 4 tables.
The main lb_item table which defines, unsurprisingly, an item.
Many fields but holds 3 ID fields.
itemID (key)
categoryID (not null)
patternID (can be null)
lb_pattern table which is keyed off the lb_item patternID.
lb_category table which is keyed off the lb_item categoryID.
lb_animal table which is keyed off the lb_item item ID.
So I need a select from the lb_item table joining to these other 3 tables to bring back varchar fields as I'm building a DTO.
A single left outer join works fine thus:
from lbi in lbContext.lb_item
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into g1
from j1 in g1.DefaultIfEmpty()
join lbc in lbContext.lb_category on lbi.categoryID equals lbc.categoryID
where lbi.itemID == id
select new lb_itemDTO..........
I now need to add a 2nd left outer join for the lb_animal table.
So I started to do this:
from lbi in lbContext.lb_item
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into g1
from j1 in g1.DefaultIfEmpty()
join lba in lbContext.lb_animal on j1.
But the options in VS for j1 give me only the fields within the lb_pattern table.
I need the join to read:
join lba in lbContext.lb_animal on j1.itemID equals lba.itemID
or
join lba in lbContext.lb_animal on lbi.itemID equals lba.itemID
Neither works and gives me an exception along the lines of "'NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core".
So how can I add a left outer join to the lb_animal table?
I've spent the last hour looking at various SO posts to suss it out but I just cannot seem to get my head around the solution for some reason. Feel like a newbie. And I'm sure the solution is going to be obvious!
Any help or pointers to a solution would be much appreciated.
This should work:
var ans = from lbi in lbContext.lb_item
where lbi.itemID == id
join lbp in lbContext.lb_pattern on lbi.patternID equals lbp.patternID into lbpj
from lbp in lbpj.DefaultIfEmpty()
join lba in lbContext.lb_animal on lbi.itemID equals lba.itemID into lbaj
from lba in lbaj.DefaultIfEmpty()
join lbc in lbContext.lb_category on lbi.categoryID equals lbc.categoryID
select new {
};
Persvered and finally came across a SO post which approached it in a different way and it worked.
Original SO is here:
My working code is now thus:
from lbi in lbContext.lb_item
from lbc in lbContext.lb_category
.Where(c => c.categoryID == lbi.categoryID)
from lbp in lbContext.lb_pattern
.Where(p => p.patternID == lbi.patternID)
.DefaultIfEmpty()
from lba in lbContext.lb_animal
.Where(a => a.itemID == lbi.itemID)
.DefaultIfEmpty()
where lbi.itemID == id
select new lb_itemDTO
Would still be interested in comments on this solution as it's not breaking the outer joins into grouped segments. So is this solution I found any less efficient in terms of the SQL it generates compared to how I was originally proposing to do it?

SQL to LINQ troubles

I have some SQL which returns two columns, the X column and Y column:
SELECT TOP (100) PERCENT
dbo.SurveyAnswer.QuestionAnswer AS [Y],
COUNT(dbo.SurveyAnswer.QuestionAnswer) AS [X]
FROM
dbo.SurveyAnswer
INNER JOIN dbo.SurveyQuestion ON
dbo.SurveyAnswer.QuestionID = dbo.SurveyQuestion.QuestionID
INNER JOIN dbo.FieldAgentCall ON
dbo.SurveyAnswer.JobId = dbo.FieldAgentCall.JobId AND
dbo.SurveyAnswer.ObjectiveId = dbo.FieldAgentCall.ObjectiveID AND
dbo.SurveyAnswer.AgentId = dbo.FieldAgentCall.AgentID
INNER JOIN dbo.SurveyQuestionaire ON
dbo.FieldAgentCall.JobId = dbo.SurveyQuestionaire.JobId and
dbo.SurveyQuestion.QuestionaireID = dbo.SurveyQuestionaire.QuestionaireID and
WHERE
(dbo.SurveyQuestion.QuestionNo = 9) AND (dbo.SurveyQuestion.QuestionaireID = 1) AND
dbo.SurveyAnswer.QuestionAnswer <>'NA'
GROUP BY
dbo.SurveyAnswer.QuestionAnswer
ORDER BY
[Y]
The SQL searches through a range of tables and returns all the answers to a question and groups then, so the results would look similar to.
X | Y
No | 234
Yes | 43
The SQL works fine, I got that working without a problem, due to the length of the query and different parameters being sent in, the query got to an unmanageable size and decided it's time it became LINQ.
So I am trying to get the basic LINQ working to get results out, but being fairly new to LINQ, I can't quite get it working
var query = (from answers in db.SurveyAnswerModels.ToList()
join question in db.SurveyQuestion.Where(i => i.QuestionID == 9 && i.QuestionaireID == 1).ToList() on answers.QuestionID equals question.QuestionID
join questionnaire in db.SurveyQuestionnaire.ToList() on question.QuestionaireID equals questionnaire.QuestionaireID
join fieldagent in db.FieldAgentCall.ToList() on questionnaire.JobId equals fieldagent.JobId
group answers.QuestionAnswer by answers.QuestionAnswer into results
select new { X = results.Count(), Y = results });
The result I am getting for this is the wrong amount of counts for X and the Y data isn't group
[{"Xs":2814,"Ys":["No","No","No","No",
Though it's the wrong amount because I assume I've not added the right parameters yet, so that's something I can sort, the main problem I am having though is the group by, I tried to replicate it as much as possible but failed.
The "No's" should just be a "No" with the count of how many No's there are, which it's going with the counter as it says there are 2,814 No's, I just need it to only say say "No" once.
Any advice would be great too, like where I am going wrong.
Try this:
var query = (from answers in db.SurveyAnswerModels
join question in db.SurveyQuestion on answers.QuestionID equals question.QuestionID
join questionnaire in db.SurveyQuestionnaire on question.QuestionaireID equals questionnaire.QuestionaireID
join fieldagent in db.FieldAgentCall on questionnaire.JobId equals fieldagent.JobId
where question.QuestionID == 9 && question.QuestionaireID == 1
group answers.QuestionAnswer by answers.QuestionAnswer into results
select new { Count = results.Count(), Answer = results.Key });
Differences from yours:
The ToLists() are removed (this is at best unnecessary and at worst will screw up the C#-expression-to-SQL translation)
Moved the Where() down to the bottom (unnecessary, but makes it easier to follow)
Select results.Key as the answer. Key is the the "grouped by" value for reach result group.
I think 3. is possibly the only step necessary to get it working.

How to Get Top 5 Rated User in 2 Tables with LINQ using C#

I have 2 tables, one is Posts another is Comments. These tables contain "RatedPoint" field.
I want to take 5 users who have the highest point.
For example, user ID =1 and its total point 50 in Post table
and it's total point is 25 in Comment table, so its total point is 75
so, i have to look whole members and after choose 5 highest point
It seems a bit complicated, i hope its clear..
I tried something like that
var abc= csEntity.Users.Where(u => csEntity.Posts.Any(p => u.Id == p.UserId)).
Take(userCount).OrderByDescending(u => u.Posts.Count).ToList();
or..
var xyz = csEntity.Posts.Where(p => csEntity.Comments.Any(c => c.UserId == p.UserId));
I dont want to use 2 different list if possible.. is it possible to do it in one query?
I could do it with 2 for loops, but i think its a bad idea..
Post TABLE
Comments TABLE
As you see, these two tables contain userID and each user has RatedPoint...
I think now its clear
EDIT: Maybe a user never write a comment or never write a post just write a comment.. then i think we musnt make equal posts.userId=comments.UserId
Here is a LINQ expression that does what you seem to be asking for:
var result = from p in posts
join c in comments on p.Id equals c.Id
select new { Id = p.Id, Total = p.Points + c.Points };
That provides the actual joined data. Then you can pick the top 5 like this:
result.OrderByDescending(item => item.Total).Take(5)
Note that the above does assume that both tables always have each user, even if they didn't post or comment. I.e. they would simply have a point count of 0. Your updated question clarifies that in your case, you have potentially disjoint tables, i.e. a user can be in one table but not the other.
In that case, the following should work for you:
var leftOuter = from p in posts
join c in comments on p.Id equals c.Id into groupJoin
let c = groupJoin.SingleOrDefault()
select new { Id = p.Id, Total = p.Points + (c == null ? 0 : c.Points) };
var rightAnti = from c in comments
join p in posts on c.Id equals p.Id into groupJoin
let p = groupJoin.SingleOrDefault()
where p == null
select new { Id = c.Id, Total = c.Points };
var result = leftOuter.Concat(rightAnti);
The first LINQ expression does a left outer join. The second LINQ expression does a left anti-join (but I call it "right" because it's effectively the right-join of the original data :) ). I'm using SingleToDefault() to ensure that each user is in each table once at most. The code will throw an exception if it turns out they are present more than once (which otherwise would result in that user being represented in the final result more than once).
I admit, I don't know whether the above is the most efficient approach. I think it should be pretty close, since the joins should be optimized (in objects or SQL) and that's the most expensive part of the whole operation. But I make no promises regarding performance. :)

a simple SQL join in LINQ not working

Consider these 2 classes.
class OrderDetail {int Specifier;}
class Order
{
OrderDetail[] Details;
}
Now I have a List I want to enumerate over, selecting only the objects with a Specifier of 1. As this is easy in SQL I thought LINQ with a join would be good for this but I dont know how to build the query.
from o in orders join o in o.Details on o.Id equals od.Id where od.Specifier = 1 select od
This gives an error that 'o' does not exist in the current context after join.
What am I doing wrong here?
orders.SelectMany(o => o.Details.Where(od => od.Specifier == 1))
In your query you are using same sequence variable name o for details, you should use join od in o.Details. But join is not needed here, because order already contains details:
from o in orderes
from od in o.Details
where od.Specifier == 1
select od

using "greater than or equal" operator in linq join operation [duplicate]

I had tried to join two table conditionally but it is giving me syntax error. I tried to find solution in the net but i cannot find how to do conditional join with condition. The only other alternative is to get the value first from one table and make a query again.
I just want to confirm if there is any other way to do conditional join with linq.
Here is my code, I am trying to find all position that is equal or lower than me. Basically I want to get my peers and subordinates.
from e in entity.M_Employee
join p in entity.M_Position on e.PostionId >= p.PositionId
select p;
You can't do that with a LINQ joins - LINQ only supports equijoins. However, you can do this:
var query = from e in entity.M_Employee
from p in entity.M_Position
where e.PostionId >= p.PositionId
select p;
Or a slightly alternative but equivalent approach:
var query = entity.M_Employee
.SelectMany(e => entity.M_Position
.Where(p => e.PostionId >= p.PositionId));
Following:
from e in entity.M_Employee
from p in entity.M_Position.Where(p => e.PostionId >= p.PositionId)
select p;
will produce exactly the same SQL you are after (INNER JOIN Position P ON E..PostionId >= P.PositionId).
var currentDetails = from c in customers
group c by new { c.Name, c.Authed } into g
where g.Key.Authed == "True"
select g.OrderByDescending(t => t.EffectiveDate).First();
var currentAndUnauthorised = (from c in customers
join cd in currentDetails
on c.Name equals cd.Name
where c.EffectiveDate >= cd.EffectiveDate
select c).OrderBy(o => o.CoverId).ThenBy(o => o.EffectiveDate);
If you have a table of historic detail changes including authorisation status and effective date. The first query finds each customers current details and the second query adds all subsequent unauthorised detail changes in the table.
Hope this is helpful as it took me some time and help to get too.

Categories

Resources