LINQ: Join conditionally to two different tables depending on each item - c#

Is there any way to set if in a linq statement?
return(from x in db.products where x.id == id
if(x.type == 1){
join y in db.category1 on x.idItem equals y.id
}else if(x.type == 2){
join z in db.category2 on x.idItem equals z.id
}
select New {....}).ToList();
I know this code is wrong but my question is:
What's the best way to implement this?

Note, that the following does not solve the problem that the OP is having because the join predicate depends on each item. The following helps if the condition is known for the entire query at once:
You split the query:
var part1 = from x in db.products where x.id == id select x;
var part2 =
b ? (from x in part1 join db.category1 select { x, joinedItem }) :
(from x in part1 join db.category2 select { x, joinedItem });
Quickly written up. You need to make the anonymous types on both queries compatible. That's the only important thing.

You could do a LEFT JOIN and one of the conditions of the LEFT JOIN could be the condition you have in the IF clause. So, you always do all the LEFT JOINs but they will only return results when the condition you have in the IF cluase is true.
Another way, with much better performance, is to create a Stored Procedure and call it from EF.

Related

LINQ Select newest records that have distinct ForeignKeyId column

I have the following SQL query:
SELECT
table1.Id AS EinAusgangId,
table1.Ausgabedatum,
table1.Rueckgabedatum,
table1.WerkzeugId,
cpmWerkzeug.Name
FROM cpmEinAusgang AS table1
INNER JOIN cpmWerkzeug ON table1.WerkzeugId = cpmWerkzeug.Id
WHERE table1.Id = (SELECT MAX(Id) AS Expr1
FROM dbo.cpmEinAusgang
WHERE table1.WerkzeugId = WerkzeugId)
My aim is to convert the whole query into a LINQ statement for further use in a .Net Application. I already converted joined tables to LINQ but is it also possible to use a select in the where clause?
This is what I got so far, which gives me almost the same result as the SQL statement above, but has major errors when the table cpmEinAusgang contains more then one record for one cpmWerkzeug
using (var dbContext = new cpmEntities())
{
var werkzeuge = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where e.Rueckgabedatum == null
orderby w.Name
select w;
return werkzeuge.ToList();
}
Has anyone an idea how to achieve the above sql in linq?
Thanks for your help. :)
EDIT:solved (see below)
var werkzeugeImUmlauf = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where e.Id == dbContext.cpmEinAusgang.Where(x => x.WerkzeugId == e.WerkzeugId).Max(x => x.Id) select w;
This is the final solution. As mentioned by Mittal in his answer, it is possible to write a sub-query in LINQ.
Yes, you can write Sub Query in LINQ as well.
var werkzeuge = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where w.id = (dbContext.cpmEinAusgang.Max(x => x.id)) AND w.WerkzeugId = WerkzeugId

join not returning all elements in left table C# lambda

I have 2 tables, the left table has data like this:
I do a left join with another table with the following expression:
var result = posicion.Join(fact,
p => p.Cod_articulo,
f => f.Cod_articulo,
(p, f) => new { p.Posicion, p.Cant_historico,
p.Cod_articulo, f.Cantidad_facturada });
The problem is that the results don't include some items from the left table as seen below:
As you can see in the result there is no data for position 3, 6 etc. what would be missing from my join?
You need to do group join (that is left join in Linq). It's better to do with query syntax:
from p in posicion
join f in fact
on p.Cod_articulo equals f.Cod_articulo into g // GroupJoin
from pf in g.DefaultIfEmpty()
select new {
p.Posicion,
p.Cant_historico,
p.Cod_articulo,
Cantidad_facturada = (pf == null) ? null : pf.Cantidad_facturada
}
This query selects all facts corresponding to posicion p into group g. Then from each group we select results, even if there is no corresponding facts for current posicion (that is DefaultIfEmpty case).
Lambda syntax will be much less readable:
posicion.GroupJoin(fact,
p => p.Cod_articulo,
f => f.Cod_articulo,
(p, g) => new { p, g })
.SelectMany(x => x.g.DefaultIfEmpty(), (x, pf) => new {
x.p.Posicion,
x.p.Cant_historico,
x.p.Cod_articulo,
Cantidad_facturada = (pf == null) ? null : pf.Cantidad_facturada
});
Consider also reading this MSDN article: How to: Perform Left Outer Joins
what would be missing from my join?
Presumably there are no entries in fact which have the corresponding Cod_articulo values (e.g. 60155 for posicion 3). Join in LINQ represents an inner join, where there has to be an entry in both sources in order to create an appropriate result.
If you want a left join, you'd typically use GroupJoin, so that each element on the "left" side ends up matching a group of entries from the "right" side, where that group may be empty.

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.

C# LINQ: How to stack LINQ queries correctly

I have a form that allows the user to perform a myriad of searches. The table(s) that need to be joined differ depending on the search criteria entered. (My example below is very simplistic because both tables use the same sub-tables to join on, but the actual problem is not as simple.)
I've been using a technique I call LINQ stacking, like this:
IQueryable<LogENT> results = Context.AssignedLogsENT.Where(l => l.AgencyId);
if(txtFirstName.Text != null)
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
p.FirstName.StartsWith(Object.FirstName)
select r;
if(txtLastName.Text != null)
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
p.LastName.StartsWith(Object.LastName)
select r;
So you see if a certain text field is set, I add to the query as necessary. This actually works fine, except that when I use SQL Profiler to view the generated query, it is INNER JOINing the tables each time I add a new criterion.
i.e. the LogAssignments table is included 3, 4, 5 times. Is there a way I can prevent it from JOINing the same table more than once?
Or, is there a better way I can do this? I've looked at Predicate Builder however it doesn't seem to permit joining tables, which is a requirement in my case.
Thanks!
IQueryable<LogENT> results = Context.AssignedLogsENT.Where(l => l.AgencyId);
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted
select r;
if(txtFirstName.Text != null)
results = from r in results
p.FirstName.StartsWith(Object.LastName)
select r;
if(txtLastName.Text != null)
results = from r in results
p.LastName.StartsWith(Object.LastName)
select r;
If you use just one query, you could modify it something like this:
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
(txtFirstName.Text != null || p.FirstName.StartsWith(Object.FirstName)) &&
(txtLastName.Text != null || p.LastName.StartsWith(Object.LastName))
select r;
You can build your base result and then dynamically add the where clauses.

LINQ join with OR

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.

Categories

Resources