Multiple Join Statements with LEFT JOIN to LINQ Expression syntax - c#

I have a slightly complicated SQL query that I'm trying to convert to LINQ Expression syntax as that is what we use for our code base. Everyone online seems to use query syntax though, which is making finding the right answer quite difficult.
The query is as follows and returns exactly what I'm after (there's probably a nicer way to do this query, please feel free to suggest one);
SELECT e.*, ce.Certificate_ID
FROM FCERTSTest.dbo.Entities AS e
INNER JOIN FCERTSTest.dbo.RequirementEntries AS re
ON re.Position_ID = e.EntityPosition_ID
LEFT JOIN FCERTSTest.dbo.CertificateEntries AS ce
ON ce.Entity_ID = e.EntityID AND ce.Certificate_ID = re.Certificate_ID
WHERE ce.Certificate_ID IS NULL
The problem is converting this. So far all I've got is;
List<Entities> unqualified = new List<Entities>();
unqualified = Entities.Join(RequirementEntries,
ent => ent.EntityPosition_ID,
req => req.Position_ID,
(ent, req) => ent).ToList();
Which I pulled from the Internet...Ihonestly don't understand the query 100% but it gets Entities who's Position has a Requirement, which is what it's meant to do.
So in closing, if someone could help me convert the rest of the SQL statement, it would be much appreciated.

This is how could your original query look in LINQ method syntax:
unqualified = Entities.Join(RequirementEntries,
ent => ent.EntityPosition_ID,
req => req.Position_ID,
(e, r) => new {e,r})
.GroupJoin(CertificateEntries.Where(c=>c.CertificateID == null),
req => new{ Cid = (int?) req.r.Certificate_ID, Eid = (int?) req.e.EntityID },
cer => new{ Cid = (int?) cer.Certificate_ID, Eid = (int?) cer.EntityID },
(x,y) => new {EnRe = x, Cer = y })
.SelectMany(x=> x.Cer.DefaultIfEmpty(),
(x,y) => new { Ent = x.Enre.e, Certs = y});
The GroupJoin is here equivalent of SQL LEFT JOIN.
IMHO, the method syntax is awkward for such complicated joins. The query syntax would be far more readable.

Related

Joining 6 tables together with inner joins & a left outer join - LINQ

I have 6 tables. Like this:
Here is the SQL code that I'm trying to re-code within LINQ.
SELECT dbo.TimeTable.Day, dbo.TimeTable.StartTime, dbo.TimeTable.Duration, dbo.Module.ModuleRef, dbo.Module.ModuleName, dbo.Course.CourseRef, dbo.Room.RoomRef, dbo.Room.RoomName,
dbo.Room.RoomFloor, dbo.Room.RoomNumber, dbo.Building.BuildingRef, dbo.Building.BuildingName
FROM dbo.Room INNER JOIN
dbo.TimeTable INNER JOIN
dbo.Module ON dbo.TimeTable.ModuleId = dbo.Module.Id ON dbo.Room.Id = dbo.TimeTable.RoomId INNER JOIN
dbo.Building ON dbo.Room.BuildingId = dbo.Building.Id LEFT OUTER JOIN
dbo.Course INNER JOIN
dbo.CourseModule ON dbo.Course.Id = dbo.CourseModule.CourseId ON dbo.Module.Id = dbo.CourseModule.ModuleId
If anyone could point me in the right direction of converting this to a LINQ statement? I am new to this concept of linq statements. Thanks for any help!
Try and not get overwhelmed with the fact that you have 6 tables and inner and left joins. Try to learn the concepts of joining 2 collections (Inner and Left) and then its just a matter of chaining linq together. Now the code can look a bit complicated but it really isn't.
Given the following in memory objects:
var rooms = new List<Room>();
var timeTables = new List<TimeTable>();
var modules = new List<Module>();
var buildings = new List<Building>();
var courses = new List<Course>();
var courseModules = new List<CourseModule>();
Your linq query might look like the following:
var result = rooms
.Join(timeTables,
room => room.Id,
table => table.RoomId,
(room, table) => new {room, table})
.Join(modules,
arg => arg.table.ModuleId,
module => module.Id,
(room_table, module) => new {room_table, module})
.Join(buildings,
arg => arg.room_table.room.BuildingId,
building => building.Id,
(room_table_module, building) => new {room_table_module, building})
.GroupJoin(courseModules,
arg => arg.room_table_module.module.Id,
coursemodule => coursemodule.ModuleId,
(room_table_module_building, coursemodules) => new { room_table_module_building, coursemodules})
.SelectMany(arg => arg.coursemodules.DefaultIfEmpty(),
(arg, coursemodule) => new { arg.room_table_module_building, coursemodule })
.Join(courses,
arg => arg.coursemodule.CourseId,
course => course.Id,
(room_table_module_building_coursemodule, course) => new { room_table_module_building_coursemodule, course });
The great part of LinqPad is that you have direct access to the db objects and can play around with your linq queries and see the generated sql. You can then take the sql and ensure that the execution plan looks good and can add any indexes that will optimize your queries.

How can I use a method withing a linq query?

Here is my query
return (from l in Context.DctLink
join t in Context.DctTabel on l.BType equals t.DocType
join t2 in Context.DctTabel on l.TabelNr equals t2.TabelNr
where l.AType == docType || l.AType == 0
select new { t.TabelNr, t2.Naam, t.Titel })
.Union(from l in Context.DctLink
join t in Context.DctTabel on l.AType equals t.DocType
join t2 in Context.DctTabel on l.TabelNr equals t2.TabelNr
where l.BType == docType || l.BType == 0
select new { t2.TabelNr, t2.Naam, t.Titel })
.Join(Context.TimIcon.Where(q => q.Timweb && q.ShowId.ToInt32() > 0),
x => x.TabelNr,
y => y.TabelNr,
(x, y) => new LookupItem
{
Id = x.TabelNr,
Name = x.Titel,
Tag = x.Naam
}).ToList();
I want to be able to do this q.ShowId.ToInt32() > 0. But I get a System.Unsupported Exception. Isn't this possible in a link query or am I just overlooking something simple
Thanks in advance
You need to fetch the data from db using AsEnumerable or ToList, then you can use any method you want. Otherwise it's not possible because EF Query Provider can't know how to translate your method into SQL.
It depends on your LINQ provider. LINQ to Objects supports pretty much anything. The one you're using (LINQ to Entities or LINQ to SQL or something similar) doesn't support everything, because it needs to understand your expression and translate it to SQL (it can't do that with any kind of expression).
The simplest way to fix this is to call AsEnumerable() at some point, in order to convert the sequence (up to that point) to an in-memory sequence, so you'll fall back to LINQ to Objects and you can perform the (previously unsupported) logic on it.

What is the linq equivalent of the below sql query

select Productid from categories where `categoryname` in `('abc','def','ghi')`;
I have tried this:
var res = from catg in db.Categories where catg.CategoryId.ToString().Contains(SelectedProducts) select catg;
But this doesnt seem to work...
Assuming SelectedProducts is an array of product ids (integers):
var cats = db.Categories.Where(o => SelectedProducts.Contains(o.CategoryId));
var pids = cats.Select(o => o.ProductId);
Reason: SQL IN operator is implemented oppositely in LINQ to SQL. The question highlights a common mistake in LINQ developers trying to translate from SQL, expecting an [attribute] [operator] [set] syntax.
Using an abstract set language we can highlight syntax differences
SQL uses a "Element is included in Set" syntax
LINQ uses a "Set contains Element" syntax
So any IN clause must be reverted using the Contains operator. It will translate to attribute IN (SET) anyways.
You need to use Contains on SelectedProducts
var res = from catg in db.Categories where
SelectedProducts.Contains(catg.categoryname) select catg.Productid;
Using method notation
var res = db.Categories.Where(catg => SelectedProducts
.Contains(catg.categoryname)).Select(catg.Productid);
The equivalence of a SQL IN with IEnumerable.Contains():
var res = from catg in db.Categories
where new[] {"abc","def","ghi"}.Contains(catg.categoryname)
select catg.Productid
Or lambda
db.Categories.Where(x => new[] {"abc","def","ghi"}.Contains(x.categoryname)).Select(c => c.ProductId);

How to use CAST,CONVERT and isNULL in LINQ?

I have the query:
SELECT TOP 50 CONVERT(date, o.OrderDate)as OrderDate,ISNULL(rd.SerialNumbers,'') as SerialNumbers,CAST(o.SourceOrderID as varchar(50)) as SourceOrderNumber
From Orders o
Query is edited for question.
var lq= (
from o in db.Orders
select new {,o.Name, o.Company, o.Address, o.Address2, o.City, o.State, o.Country, o.Email, o.Zip, o.Phone, o.ShipName, o.ShipCompany, o.ShipAddress, o.ShipAddress2, o.ShipCity, o.ShipCountry, o.ShipState, o.ShipPhone, o.ShipZip, o.OrderNumber, o.ShippingTotal }
).ToList();
I can make simple joins and select in LINQ but not getting idea how to get selects like one mentioned in query.
I am using EF
Given there is no way to actually perform a string to datetime conversion as part of the DB query using LINQ, it's recommended you use AsEnumerable to switch the context of the list to run the actual conversion in memory
orders.Take(50)
.AsEnumerable()
.Select(x => new {
OrderDate = x.OrderDate.Date,
SerialNumbers = o.SerialNumbers ?? "",
SourceOrderNumber = o.SourceOrderID.ToString()
});
LINQ2SQL is actually bright enough to handle the parse in the code:
var q = from c in Customers
where c.PhoneNumber == "9075556658"
select new
{
SSN = c.SSN,
DOB = DateTime.Parse(c.BirthDate)
};
q.Dump();
I just run that in LinqPad on my own database, and it worked fine.
NOTE: This was tested with Linq2SQL, not Entity Framework, which has a history of not be able to do things Linq2SQL has been doing for years.

Convert this NHibernate query with queryover

I have some problem to convert this NHibernate queries into the left join queryover
var query = session.Query<T>.Join(
Session.Query<RecordOrder>(),
q=>q.MiniDbName,
o=>o.DatabaseName,
(q,o)=>new{Record = q, Order = o.OrderValue})
Anyone can help me, I want this query support the left join.
The default join is an inner-join. Each of the additional join types can be specified using the methods .Inner, .Left, .Right, or .Full. For example, to left outer-join on Kittens use:
IQueryOver<Cat,Kitten> catQuery =
session.QueryOver<Cat>()
.Left.JoinQueryOver(c => c.Kittens)
.Where(k => k.Name == "Tiddles");
In your case :
var list =
session.QueryOver<RecordOrder>()
.Left.JoinQueryOver(c => c.Orders).ToList()

Categories

Resources