EF Core CROSS JOIN with Multiselect and Sub Query - c#

I have a problem writing a LINQ query using CROSS JOIN on two tables, and subquery in the result.
I'll show you the SQL query that I made, and it is working in my PostgreSQL Server:
SELECT t2.id, t1.id,
(
SELECT COALESCE(avg(t3.value), -1)
FROM table3 t3
LEFT JOIN table4 t4 ON t4.id = t3.tag_id
LEFT JOIN table5 t5 ON t5.id = t4.device_id
LEFT JOIN table6 t6 ON t6.id = t4.system_id
WHERE t4.tag_type_id = 171 --TT Value
AND t1.id = ANY(t4.control_area)
AND t6.zone_id = t2.id
)
FROM table1 t1
CROSS JOIN table2 t2
ORDER BY t2.z_index,t1.d_index
And I try now to duplicate this query in EF Core code and this is what I got so far:
var ss = ctx.Table1.SelectMany(
t1 => ctx.Table2,
(t1, t2) => new
{
T1 = t1.Id,
T2 = t2.Id,
Value = ctx.Table3
.Include(s => s.Table4.Table5.Table6)
.Where(s => s.Table4.TagTypeId == 171
&& s.Table4.Table5.ControlArea.Any(a => a == t1.Id)
&& s.Table4.Table5.Table6.Zone == t2.Id)
}).ToList();
When I run this I get the following error:
Unable to cast object of type 'System.Linq.Expressions.MethodCallExpression2' to type
'System.Linq.Expressions.LambdaExpression'.
I'm not sure how can I accomplish this without using ctx.Database.ExecuteSqlRaw("SELECT ....");
Is there a way to do it?
Thanks in advance,
Julian

I was able to solve my problem. I'm posting this answer with hope that it will help others with similar problem. The solution is just to add .Average(s=>s.Value) to the end of the subquery.
Something like this:
var ss = ctx.Table1.SelectMany(
t1 => ctx.Table2,
(t1, t2) => new
{
T1 = t1.Id,
T2 = t2.Id,
Value = ctx.Table3
.Include(s => s.Table4.Table5.Table6)
.Where(s => s.Table4.TagTypeId == 171
&& s.Table4.Table5.ControlArea.Any(a => a == t1.Id)
&& s.Table4.Table5.Table6.Zone == t2.Id).Average(s=>s.Value)
}).ToList();

Related

Query multiple tables with LINQ, grab results to dictionary

I have this simple SQL:
SELECT name, value
FROM table1 t1, table2 t2
WHERE t1.id = t2.id
AND t1.id = 123
How do I convert it to LINQ in C#, so that the results are in Dictionary?
I'm guessing you are using Entity Framework?
Could you use RelationShips instead?
Or like this:
var query = from t1 in context.Table1
from t2 in context.Table2
where t1.Id == t2.Id
select new { t1.name, t2.value};
var result = query.ToList();
var dir = result.ToDictionary(x => x.name, x => x.value);
https://learn.microsoft.com/en-us/ef/core/querying/complex-query-operators

Convert nested SQL query to LINQ Query

Is there a way to accurately convert the following SQL query to LINQ
SELECT * FROM T1
WHERE ColumnA IN (
SELECT FkColumnA FROM T2
WHERE FkColumnB IN (
SELECT ColumnB FROM T3
WHERE FkColumnC IN (
SELECT ColumnC FROM T4
WHERE FkColumnD = 1)) AND FkColumnE is null AND ColumnF = 0)
Also, does anyone know of any documentation wherein any logic or guideline to convert SQL queries to LINQ is laid out?
EDIT 1:
The equivalent for the above using JOINS would be as below:
select * from T1 a
inner join T2 b on a.FKColumnA = b.ColumnA
inner join T3 c on c.ColumnB = b.FkColumnB
inner join T4 d on d.ColumnC = c.FkColumnC
where a.FkColumnD is null and a.ColumnE = 0
and d.ColumnC = 1
and it's equivalent LINQ query would be
var linq = from q in context.T1
join r in context.T2
on q.FKColumnA equals r.ColumnA
join s in context.T3
on r.FkColumnB equals s.ColumnB
join t in context.T4
on s.FkColumnC equals t.ColumnC
where q.FkColumnD != null && q.ColumnE == false && t.ColumnC == 56816
select q.FkColumnF;
But using JOINS looked to be a bit more simpler and better in LINQ. Thus the question is for my knowledge purpose only.
Translating your query literally, we get the following LINQ statement:
var results = table1.Where(t1 => table2.Where(
t2 =>
table3.Where(
t3 =>
table4.Where(t4 => t4.FkColumnD == 1)
.Select(t4 => t4.ColumnC)
.Contains(t3.FkColumnC))
.Select(t3 => t3.ColumnB)
.Contains(t2.FkColumnB) && !t2.FkColumnE.HasValue && t2.ColumnF == 0)
.Select(t2 => t2.FkColumnA)
.Contains(t1.ColumnA));
This results in an IEnumerable<T1> which you can use as required.
As far as I know there is no "documentation" on converting syntax, this, as a developer, is your job. However, I personally find LINQPad very useful when constructing LINQ statements.

How can a LINQ join select only the first record?

I wish to select only the first record from the 'CustomerSubOwners' table in join query below and wondered what was the best way to achieve this in LINQ.
var result= (from t1 in db.Cases
from t2 in db.CustomerSubOwners
.Where(o => t1.CustomerId == o.CustomerId && o.Expiry >= DateTime.Now)
.DefaultIfEmpty()
select t1);
I think you are looking for the Take method like so:
var result= (from t1 in db.Cases
from t2 in db.CustomerSubOwners.Where(o => t1.CustomerId == o.CustomerId && o.Expiry >= DateTime.Now)
.Take(1)
.DefaultIfEmpty()
select t1);

as performed query SQL in linq or lambda expression?

Good day to all,
I have the following problem
I have to convert the following query SQL to LINQ to SLQ or lambda expression:
SELECT T2.ID, SUM(T1.Importe) AS Importe, T3.ID, T3.Column_1,
T3.Column_2, T1.Column_3
FROM Tabla_1 T1
INNER JOIN Tabla_2 T2 on T1.ID = T2.ID
INNER JOIN Tabla_3 T3 on T2.ID = T3.ID
WHERE T1.ID in (LIST)
GROUP BY T2.ID, T3.ID, T3.Column_1, Column_2, T1.Column_3
where LIST is a List (12,15,18,19,...)
as performed in linq or lamnbda expression?
thks!
Here is the hint:
var result = ents.T1
.Where(x => list.Contains(x.Id))
.GroupBy(x => new
{
Id2 = x.T2.Id,
Id3 = x.T3.Id,
...
// etc group fields
})
.Select(x => new
{
Importe = x.Sum(i => i.Importe)
x.Key.Id2,
// other group fields
...
})
.ToArray();
I strongly suggest not to name columns and tables the way you did. Really)
I did not try, but it could look like that :
var result = (from t1 in dataContext.tabla_1
join t2 in dataContext.tabla_2 on t1.ID equals t2.ID
join t2 in dataContext.tabla_2 on t2.ID equals t3.ID
group t2 by t2.ID into g
where listIds.contains(t1.ID)
select new {g.ID, g.Sum(t => t.Importe), ...});

Convert regular SQL to LINQ to Entities

After a lot of search, I cannot find a simple answer to this following SQL Statement :
SELECT t1.LoginName, t0.BNAME
FROM USR02 AS t0
LEFT OUTER JOIN LoginData AS t1
INNER JOIN Mandants AS t2 ON t1.Id_Mandants = t2.Id_Mandants
ON t0.BNAME = t1.LoginName AND t0.MANDT = t2.CodeMandant
Because of the double ON statement I don't know how to write it in LINQ.
I've tried to simplify it but multiple primary key make the job hard.
Start by translating the SQL query to one that is more natural. Like this:
SELECT t1.LoginName, t0.BNAME
FROM USR02 AS t0
LEFT OUTER JOIN LoginData AS t1
ON t0.BNAME = t1.LoginName
INNER JOIN Mandants AS t2
ON t1.Id_Mandants = t2.Id_Mandants
WHERE t0.MANDT = t2.CodeMandant
Now it should be easy to translate this to LINQ. When you have you have set up the relationships in your entity model correctly, you would be able to write the following LINQ query:
from data in db.LoginData
where data.User.MANDT == data.Mandant.CodeMandant
select new { data.LoginName, data.User.BNAME };
btw. Why are you outputting both LoginData.LoginName as USR02.BNAME since they are always equal?
You need to convert your query to regular, 1-level join:
select t1.LoginName, t0.BNAME
from USR02 as t0
left outer join LoginData as t1 on t0.BNAME = t1.LoginName
inner join Mandants as t2 on t0.MANDT = t2.CodeMandant and t1.Id_Mandants = t2.Id_Mandants
Then it will be much easier to rewrite it on LINQ to Entities:
from t0 in db.t0
join t1 in db.t1 on t0.BNAME equals t1.LoginName
join t2 in db.t2 on new { t0.MANDT, t1.Id_Mandants} equals new { t2.CodeMandant , t2.Id_Mandants }
select new { t1.LoginName, t0.BNAME };
I like writing joins in this way
from t0 in db.t0
from t1 in db.t1.Where(x => t0.BNAME == x.LoginName).DefaultIfEmpty()
from t2 in db.t2.Where(x => t0.MANDT == x.CodeMandant)
.Where(x => t1.Id_Mandants == x.Mandants)
select new { t1.LoginName, t0.BNAME };

Categories