Linq group join and where statement on property of the joined table - c#

So I believe I have found out that group join is a left outer join and that is what I need. But I need to check if the joined tables property is null. But I haven't got it working yet.
So I basically need the equivalent of this query in Linq Entity Framework
SELECT
id, test, test2
FROM Table1
LEFT OUTER JOIN Table2 ON
table1.id = table2.id
WHERE table2.example = NULL;
I have tried to do this with lambda but without any success yet. I can't seem to get the hold of the table2 property example for the where statement.

You can flow this example using LINQ Extension Method (GroupJoin):
Table1.GroupJoin(Table2,
x => x.ID,
y => y.ID,
(tbl1, tbl2) => new {Table1=tbl1, Table2 =tbl2.DefaultIfEmpty()})
.SelectMany(
tbl => tbl.Table2.Where(t2 => t2.example == null).Select(x => new
{
id= tbl.Table1.ID,
test = tbl.Table1.Test,
test2 = tbl.Table2.Test
}))ToList();

You might want to check out: http://www.sqltolinq.com/
Linqer is a SQL to LINQ converter tool. It helps you to learn LINQ and convert your existing SQL statements.
Not every SQL statement can be converted to LINQ, but Linqer covers many different types of SQL expressions.
Lets assume you have Table1 and Table2 in an EF dbcontext.
from Table1 in context
from Table2 in context
.Where(t2=> t2.ID == Table1.ID && t2.example == null).DefaultIfEmpty()
select new
{
id= Table1.ID
,test = Table1.Test
,test2 = Table2.Test
}

Related

C# LINQ INNER JOIN is returning wrong count

I have the following sql with JOIN but which returns a record count for 50 but when I convert it to LINQ, I am not getting the matching count. I noticed that when I add the ON clause, the visual studio intellisense dropdown does not show the ID property for the 2nd table. I am wondering if that is an issue.
Here is the simple SQL
SELECT * FROM Table1 T1 JOIN Table2 T2 ON T1.MyId = T2.MyId WHERE T1.IsCompleted
Here is my lamba LINQ with the comment where the VC Intellisense is not working right. For table2, the intellisense dropdown, it is only showing Equals, GetHashCode, GetType, and ToString. Just manually type the MyId and everything successfully builds but the count is too high. Thanks
var test = this.myDbContent.Table1
.Join (this.myDbContent.Table2
table1 => table1.MyId,
table2 => table2.MyId,
(table1, table2) => new { table1, table2}
)
.Where (joinedTable => joinedTable.Table1.IsCompleted == 1)
Join is the simple LINQ operation, but better understandable via LINQ Query.
var query =
from t1 in myDbContent.Table1
join t2 in myDbContent.table2 on t1.MyId equals t2.MyId
where t1.IsCompleted
select new
{
t1,
t2
};
If you still have different results, probably MyId is nullable field and EF also matches nulls.
It can be disabled via options configuration:
builder.UseSqlServer(ceConnection, x => x.UseRelationalNulls(true));

Ef core not ignoring columns in generated SQL

I am using EF for .NET Core
I have a table which I dont want to bring back all the columns for in a query
(from ae in Table1.Select(x => new {x.CreatedDate, x.EntryId, x.Amount})
join si in Table2.Select(x => new {x.SessionId, x.IdToFind})
.Where(y => y.SessionId == Guid.Parse("52F0C862-15D0-4C7A-975D-285C618342B0")) on ae.EntryId equals si.IdToFind
select new
{
create output
}).ToList();
As you can see, I am specifically only including 2 columns from Table2
SELECT [a].[EntryId], [a].[Amount]
FROM [Table1] AS [a]
INNER JOIN (
SELECT [s].[Id], [s].[CreatedBy], [s].[CreatedDate], [s].[IdToFind], [s].[SessionId], [s].[UpdatedDate], [s].[UpdateddBy]
FROM [Table2] AS [s]
WHERE [s].[SessionId] = '52f0c862-15d0-4c7a-975d-285c618342b0'
) AS [t] ON [a].[EntryId] = [t].[IdToFind]
When I use LinqPad to evaluate the generated SQL, I can see that all the columns from Table 2 have been include
How can I prevent this? I dont want to use the Ignore option on the model loading because in other situations I may want the other columns
You have to use this syntax:
ResultClass result =(from ae in Table1
join si in Table2
on ae.EntryId equals si.IdToFind
where(si.SessionId == Guid.Parse("52F0C862-15D0-4C7A-975D-285C618342B0"))
select new ResultClass
{
ae.CreatedDate,
ae.EntryId,
ae.Amount,
si.SessionId,
si.IdToFind
}).ToList();
But in this case you will need to create a new class that contains properties of both classes or you get an anonymos object that you have to use immediately.
When I like to avoid creating a new class I usually add some [NotMapped] properties to one of Table1 or Table2 classes in a partial class code and use this class instead of the ResultClass
Your approach should work, however mixing the Linq query syntax and Fluent methods isn't making it very readable at all and I suspect that may be responsible for the unexpected query generation. Particularly this:
(from ae in Table1.Select(x => new {x.CreatedDate, x.EntryId, x.Amount})
join si in Table2.Select(x => new {x.SessionId, x.IdToFind}).Where(y => y.SessionId == Guid.Parse("52F0C862-15D0-4C7A-975D-285C618342B0"))
Which has the Where condition on the Table2 Select.
From what I can deduce from your approach, you are being too eager with your optimization:
With EF queries, Select projections do not need to be done at a per-entity level, they are done at the end of the expression and EF will work out exactly what columns from what table need to be included. That said, you can pre-select columns and EF should work it all out, but it will do so from your final projection.
Provided you have an actual FK between Table1.EntryId and Table2.IdToFind, ideally you would want that relationship mapped out in the entities rather than relying on an explicit join. Navigation properties make it much easier to organize queries, but there can be more generic cases where Table2 can serve multiple other tables etc.
Assuming that you want to just build an output from the CreatedDate, EntryId, Amount, and SessionId from these two loosely related tables:
var results = context.Table1
.Join(context.Table2.Where(t2 => t2.SessionId == sessionId),
t1 => t1.EntryId,
t2 => t2.IdToFind,
(t1, t2) => new { Table1 = t1, Table2 = t2 })
.Select(x => new ResultViewModel
{
EntryId = x.Table1.EntryId,
CreatedDate = x.Table1.CreatedDate,
Amount = x.Table1.Amount,
// ...
}).ToList();
Assuming you don't even need anything from Table2 then:
// ...
.Join(context.Table2.Where(t2 => t2.SessionId == sessionId),
t1 => t1.EntryId,
t2 => t2.IdToFind,
(t1, t2) => t1)
.Select(x => new ResultViewModel
{
EntryId = x.EntryId,
CreatedDate = x.CreatedDate,
Amount = x.Amount
}).ToList();
When you read the Linq it may seem like EF would be selecting all columns from the related tables so you'd want to add Select projections there, but they are not needed. The final query should select columns strictly by the final Select projection. I would avoid mixing Linq query language (from Table1 join Table2 select ....) with the Fluent builder methods. IMO the Fluent methods are easier to read from a C# code perspective, but ultimately I'd use one or the other, not both.

LINQ - query on query results (some complex one)

Need some help in writing LINQ from the following SQL.
The main problem is double grouping.
I'm stacked in the second grouping
group by s.[e-year], s.[e-month]
Don't know how to implement.
Thanks a lot.
select s.[e-year], s.[e-month], count(s.projectid) 'projects entranced',
---------------------------------------------
(select count(subquery.CustomerTypeID) from
(select count(ap.ProjectID) as 'count', c.CustomerTypeID FROM Logging_ProjectsEntrances] pe
inner join users u on pe.userid = u.userid
inner join Companies c on u.CompanyId = c.CompanyID
inner join AssignedProjects up on pe.ProjectID = up.ProjectID
inner join Projects p on up.ProjectID = p.ProjectID
where ap.ProductID = 1 and year(pe.EntranceDate) = s.[e-year] and MONTH(pe.entrancedate) = s.[e-month] and c.CustomerTypeID = 2
group by ap.ProjectID, c.CustomerTypeID) subquery
group by subquery.CustomerTypeID
)
--------------------------------------------
from
(
select YEAR(pe.EntranceDate) as 'e-year', MONTH(pe.EntranceDate) as 'e-month', up.ProjectID as 'projectid'
FROM Logging_ProjectsEntrances pe
inner join AssignedProjects ap on pe.ProjectID = ap.ProjectID
inner join Projects p on ap.ProjectID = p.ProjectID
where ap.ProductID = 1
group by year(pe.EntranceDate), month(pe.EntranceDate), ap.ProjectID
) as s
group by s.[e-year], s.[e-month]
order by s.[e-year] desc , s.[e-month] desc
For translating SQL to LINQ query comprehension:
Translate FROM subselects as separately declared variables.
Translate each clause in LINQ clause order, translating monadic operators (DISTINCT, TOP, etc) into functions applied to the whole LINQ query.
Use table aliases as range variables. Use column aliases as anonymous type field names.
Use anonymous types (new { ... }) for multiple columns.
Left Join is simulated by using a into join_variable and doing another from from the join variable followed by .DefaultIfEmpty().
Replace COALESCE with the conditional operator and a null test.
Translate IN to .Contains() and NOT IN to !...Contains()
SELECT * must be replaced with select range_variable or for joins, an anonymous object containing all the range variables.
SELECT fields must be replaced with select new { ... } creating an anonymous object with all the desired fields or expressions.
Proper FULL OUTER JOIN must be handled with an extension method.
Note: Your SQL query is using a SQL trick (SELECT x ... GROUP BY x) to perform the equivalent of a DISTINCT, which should be used as it expresses the intent more clearly.
So, for your SQL query:
var subq = (from pe in projectsEntrances
join ap in assignedProjects on pe.ProjectID equals ap.ProjectID
join p in projects on ap.ProjectID equals p.ProjectID
where ap.ProductID == 1
select new { e_year = pe.EntranceDate.Year, e_month = pe.EntranceDate.Month, ap.ProjectID }).Distinct();
var ans = from s in subq
group s by new { s.e_year, s.e_month } into sg
orderby sg.Key.e_year descending, sg.Key.e_month descending
select new { sg.Key.e_year, sg.Key.e_month, ProjectsEntranced = sg.Count() };

Write an INNER JOIN in LINQ

I have the following SQL query. I would like to know how to write the same query in LINQ and C#.
select ph.Id,p.Id as projInfoId, ph.Title, ph.AdditionalHours, ph.AdditionalCost,
ph.InsertDate, ph.InsertBy, ph.LastUpdateDate, ph.LastUpdateBy, ph.TeamId,
ph.ProjInfoId
from tblTeamType t
join ProjInformation p on t.team_id = p.teamId
join projProject pj on p.projectId=pj.projectId
inner join ProjInfoAdditionalHrs ph on p.teamId = ph.teamId and p.Id = ph.proJinfoid
I think it is easier to translate SQL using query comprehension syntax instead of lambda syntax.
General rules:
Translate inner queries into separate query variables
Translate SQL phrases in LINQ phrase order
Use table aliases as range variables, or if none, create range
variables from table names abbreviations
Translate IN to Contains
Translate SQL functions such as DISTINCT or SUM into function calls
on the entire query.
Create anonymous objects for multi-column grouping or joining
Using these rules, you should get something like:
var ans = from t in tblTeamType
join p in ProjInformation on t.team_id equals p.teamId
join pj in projProject on p.projectId equals pj.projectId
join ph in ProjInfoAdditionalHrs on new { p.teamId, p.Id } equals new { ph.teamId, ph.proJinfold }
select new {
ph.Id,
projInfoId = p.Id,
ph.Title,
ph.AdditionalHours,
ph.AdditionalCost,
ph.InsertDate,
ph.InsertBy,
ph.LastUpdateDate,
ph.LastUpdateBy,
ph.TeamId,
ph.ProjInfoId
};

LINQ2SQL - More Questions on when a Cross join with where clause is emitted instead of Inner Join

This is a two part question and for education purposes rather than trying to find a solution to a problem.
I've seen this already and realize that it's very similar to my question
LINQ2SQL - Cross join emitted when I want inner join
But I am hoping for more information from you LINQ and SQL gurus as to why the cross join is created instead of inner join in LINQ2SQL. Additionally, can someone explain how SQL Server decides on the execution plan (or link to further information) since both of these queries generate the same plan? From what I understand, this means that the performance of the queries are the same.
I've created a small example that runs two LINQ expressions on my database that generates these two different SQL queries.
For those who don't want to bother, here's my example db diagram:
http://dl.dropbox.com/u/13256/Screen%20shot%202011-03-16%20at%2011.41.56%20AM.png
Here are the two queries:
Cross Join with Where Clause
var q = from item in context.Items
join i_mem in context.Memberships on new { item_id = item.ID, user_id =
current_user_id.Value } equals new { item_id = i_mem.RelatedItemID, user_id =
i_mem.RelatedUserID } into sq_i_m
from im in sq_i_m.DefaultIfEmpty()
join i_cat in context.Categories on item.RelatedCategoryID equals i_cat.ID
into sq_i_cat
from proj in sq_i_cat
select item;
Inner Join
from item in context.Items
join i_mem in context.Memberships on
new { item_id = item.ID, user_id = current_user_id.Value }
equals
new { item_id = i_mem.RelatedItemID, user_id = i_mem.RelatedUserID }
into sq_i_m
from im in sq_i_m.DefaultIfEmpty()
join i_cat in context.Categories on item.RelatedCategoryID equals i_cat.ID
select item
And here is the test program if you'd like to see for yourself.
Thanks for everyone's help.
Mustafa
They are the same thing, so it does not matter which LINQ2SQL emits.
An inner join is logically equivalent to a cross join with a where clause filter equivalent to the on of the inner join clause.
That's why Sql Server generates the same query plan.
To be clear, the inner join:
Select f1
From T1 inner join T2 on T1.k = T2.k
where T1.f2 like 'X%'
Is the same as the cross join:
Select f1
From T1 cross join T2
where T1.k = T2.k
and T1.f2 like 'X%'
is the same as old-style SQL:
Select f1
From T1, T2
where T1.k = T2.k
and T1.f2 like 'X%'
Lets say you have a datacontext called MyDataContext.
using(MyDataContext db = new MyDataContext())
{
var q = db.Items.Where(x=> x.Categories.Name == "myCategory").Select(x=> x);
}
This is a very simple example, but you didn't need to write out a join or a subquery in TSQL syntax. (I hate writing TSQL).

Categories