Convert SQL query to LINQ - c#

I have a beginners LINQ2SQL question. I have this huge (but not complex) SQL statement:
SELECT Artikel.ArtikelID,
Artikel.CategorieID,
Artikel.ImageFile,
Artikel.RetailPrijs,
ISNULL(ShopArtikel.VerkoopsPrijs, Artikel.VerkoopsPrijs) AS VerkoopsPrijs,
Artikel.ArtikelCode,
Artikel.InAssortimentSinds,
ArtikelTaal.ArtikelNaam,
ArtikelTaal.ArtikelOmschrijving
FROM Artikel
INNER JOIN ArtikelTaal ON Artikel.ArtikelID = ArtikelTaal.ArtikelID
INNER JOIN ShopArtikel ON Artikel.ArtikelID = ShopArtikel.ArtikelID
INNER JOIN Categorie ON Artikel.CategorieID = Categorie.CategorieID
INNER JOIN CategorieTaal ON Categorie.CategorieID = CategorieTaal.CategorieID
INNER JOIN Shop ON ShopArtikel.ShopId = Shop.ShopID
INNER JOIN CategorieGroepShop ON Shop.ShopID = CategorieGroepShop.ShopId
INNER JOIN Taal ON ArtikelTaal.TaalCode = Taal.TaalCode AND CategorieTaal.TaalCode = Taal.TaalCode
INNER JOIN CategorieGroepTaal ON Taal.TaalCode = CategorieGroepTaal.TaalCode AND CategorieGroepShop.CategorieGroepId = CategorieGroepTaal.CategorieGroepID
INNER JOIN CategorieGroep ON Categorie.CategorieGroepID = CategorieGroep.CategorieGroepID AND CategorieGroepTaal.CategorieGroepID = CategorieGroep.CategorieGroepID AND CategorieGroepShop.CategorieGroepId = CategorieGroep.CategorieGroepID
WHERE (Shop.ShopID = 23) AND
(Taal.TaalCode = 'dut') AND
(Artikel.Onzichtbaar = 0) AND
(Artikel.NietBestelbaar = 0) AND
(Categorie.Onzichtbaar = 0) AND
(Artikel.Voorraad >= Artikel.LevertijdDrempel)
and I am converting this to LINQ and have this:
var allProducts = from artikelen in dc.Artikels
join sa in dc.ShopArtikels on artikelen.ArtikelID equals sa.ArtikelID
join at in dc.ArtikelTaals on artikelen.ArtikelID equals at.ArtikelID
join cat in dc.Categories on artikelen.CategorieID equals cat.CategorieID
join catt in dc.CategorieTaals on cat.CategorieID equals catt.CategorieID
join catg in dc.CategorieGroeps on cat.CategorieGroepID equals catg.CategorieGroepID
join catgt in dc.CategorieGroepTaals on catg.CategorieGroepID equals catgt.CategorieGroepID
join sh in dc.Shops on sa.ShopId equals sh.ShopID
join catgs in dc.CategorieGroepShops on sh.ShopID equals catgs.ShopId
join tl in dc.Taals on new { tc1 = at.TaalCode, tc2 = catgt.TaalCode } equals new { tc1 = tl.TaalCode, tc2 = tl.TaalCode }
where sh.ShopID == shop.BLL.Business.ShopController.CurrentShop.Id
select dc.Artikels;
but I have the idea that I made some (minor) mistakes while joining.
any ideas please!
EDIT
I have rewritten the LINQ query to this:
var allProducts = from artikelen in dc.Artikels
join at in dc.ArtikelTaals on artikelen.ArtikelID equals at.ArtikelID
join sa in dc.ShopArtikels on artikelen.ArtikelID equals sa.ArtikelID
join cat in dc.Categories on artikelen.CategorieID equals cat.CategorieID
join catt in dc.CategorieTaals on cat.CategorieID equals catt.CategorieID
join sh in dc.Shops on sa.ShopId equals sh.ShopID
join catgs in dc.CategorieGroepShops on sh.ShopID equals catgs.ShopId
join tl in dc.Taals on new { tc1 = at.TaalCode, tc2 = catt.TaalCode } equals new { tc1 = tl.TaalCode, tc2 = tl.TaalCode }
join catgt in dc.CategorieGroepTaals on new { tl.TaalCode, catgs.CategorieGroepId } equals new { catgt.TaalCode, catgt.CategorieGroepID }
join catg in dc.CategorieGroeps on new { cat.CategorieGroepID, catgt.CategorieGroepID, catgs.CategorieGroepId } equals new { catg.CategorieGroepID, catg.CategorieGroepID, catg.CategorieGroepID }
where sh.ShopID == 230
select dc.Artikels;
but I have a syntax error after "dut" }
Edit 2:
changed the join and replaced "dut" with the corresponding field in the database.
still have the error after the first }
it says: type inference failed in the call to 'Join'

Some of the SQL joins have multiple join conditions, which you didn't put in the LINQ query.

If this is something that will be frequently run then you should rewrite it as a stored procedure. I believe it is too convoluted and complex for a LINQ statement - too hard to see what's going on.

There is a tool for it, but I didn't try it. May be it's usefull for you.
http://www.sqltolinq.com/

It looks like the error line is actually a "Where" cause but not "Joining".
You can actually split the whole long Linq statement into smaller Query.
so for this case, its better to split it like this:
var at = from a in dc.ArtikelTaals
where a.TaalCode == "dut"
select a;
var catt = from c in dc.CategorieTaals
where c.TaalCode == "dut"
select c;
.....
and you can join the IQueryable "at" and "catt" in your complex query later.

Related

Adding Count Column to LINQ query Based on Joined Table

So I have a SQL view that I've created that provides me what I need. Essentially it's a job position billeting system that shows how many positions have been authorized vs filled (or assigned).
SELECT Companies.Name AS Company, Grades.Name AS Grade, Series.Name
AS Series, Positions.Authorized, COUNT(People.PersonId) AS Assigned
FROM Companies INNER JOIN
Positions ON Companies.Id = Positions.CompanyId INNER JOIN
Series ON Positions.SeriesId = Series.Id INNER JOIN
Grades ON Positions.GradeId = Grades.Id INNER JOIN
People ON Positions.CompanyId = People.CompanyId AND
Positions.SeriesId = People.SeriesId AND Positions.GradeId = People.GradeId
GROUP BY Companies.Name, Grades.Name, Series.Name, Positions.Authorized
Now what I'd like to be able to do is recreate this in a LINQ query. I've almost got it where I need it; however, I can't figure out how to add the counted column at the end that's based on the People table.
Here's my current LINQ query:
var query = from a in db.Companies
join b in db.Positions on a.Id equals b.CompanyId
join c in db.Series on b.SeriesId equals c.Id
join d in db.Grades on b.GradeId equals d.Id
join e in db.People on new { b.CompanyId, b.SeriesId, b.GradeId } equals new { e.CompanyId, e.SeriesId, e.GradeId }
group a by new { CompanyName = a.Name, GradeName = d.Name, SeriesName = c.Name, b.Authorized, e.PersonId } into f
select new { Company = f.Key.CompanyName, Grade = f.Key.GradeName, Series = f.Key.SeriesName, f.Key.Authorized, Assigned = /* needs to be Count(People.PersonId) based on last join */ )};
Thanks in advance for any help you can provide!
Figured it out. The reason why it was posting multiple rows and not doing a proper count on the same row was because in my "group by" I added in "e.PersonId" when it should have simply been removed. I also had to add a few things to make it work on the front-end razor views since it's an anonymous type (this doesn't have anything to do with the original question, but thought I'd give reason to the changes). So the person who removed their answer, you were partially right, but the reason it wasn't working was because of the additional fieldin the group by:
dynamic query = (from a in db.Companies
join b in db.Positions on a.Id equals b.CompanyId
join c in db.Series on b.SeriesId equals c.Id
join d in db.Grades on b.GradeId equals d.Id
join e in db.People on new { b.CompanyId, b.SeriesId, b.GradeId } equals new { e.CompanyId, e.SeriesId, e.GradeId }
group a by new { CompanyName = a.Name, GradeName = d.Name, SeriesName = c.Name, b.Authorized } into f
select new { Company = f.Key.CompanyName, Grade = f.Key.GradeName, Series = f.Key.SeriesName, Authorized = f.Key.Authorized, Assigned = f.Count()}).AsEnumerable().Select(r => r.ToExpando());
And what it looks like on the page:

Multiple Left OUTER OUTER joins in LINQ to Entities

Using Linq To Entities how would I reproduce the following SQL Query?
SELECT m.MaterialId, m.MaterialName, m.MaterialTitle, vv.NearestTXDate, c.ChannelName
FROM GB_Material m
LEFT OUTER JOIN WF_VideoVersion vv on vv.MaterialID = m.MaterialID
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
INNER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
INNER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
EDIT: I've excepted an answer to this as the answer produces the exact results that this SQL Query does, but be aware that the original SQL query produces an unwanted Cross Join, which i didn't realise when i wrote it.
(from m in context.GB_Material
join vv in context.WF_VideoVersion on new {m.MaterialID }
equals new { vv.MaterialID } into vv_join
from vv in vv_join.DefaultIfEmpty()
join se in context.SP_ScheduleEvent on new {m.MaterialName }
equals new { se.MaterialName } into se_join
from se in se_join.DefaultIfEmpty()
join s in context.SP_Schedule on new {se.ScheduleID } equals new { s.ScheduleID}
join c in context.GB_Channel on new { s.ChannelID } equals new { c.ChannelID }
where
m.MaterialName.ToLower().Contains("foo") || m.MaterialTitle.ToLower() .Contains("foo")
select new
{
m.MaterialId, m.MaterialName, m.MaterialTitle, vv.NearestTXDate, c.ChannelName
})
Try this query
var objlist =(from m in Contex.GB_Material
from vv in Contex.WF_VideoVersion.Where(x=>x.MaterialID =m.MaterialID ).DefaultIfEmpty()
from se in Contex.SP_ScheduleEvent.Where(x=>x.MaterialName =m.MaterialName ).DefaultIfEmpty()
from s in Contex.SP_Schedule .Where(x=>x.ScheduleID =se.ScheduleID)
from c in Contex.GB_Channel .Where(x=>x.ChannelID =s.ChannelID )
WHERE m.MaterialName.ToLower().Contains("foo") || m.MaterialTitle.ToLower() .Contains("foo")
select new{ m.MaterialId, m.MaterialName, m.MaterialTitle, vv.NearestTXDate, c.ChannelName}).ToList();

Entity Framework Left out join taking forever (performance issues)

I'm using the entity framework and linq to do multiple left outer joins but it takes 3 seconds to run the query through the entity framework.
I used ((System.Data.Objects.ObjectQuery)query).ToTraceString(); and tested the sql statement in management studio and it executed instantly.
Here is the linq code.
var query = from Inventory in db.INVENTORies
join NewInventory in db.NEW_INVENTORY on Inventory.Barcode equals NewInventory.Barcode into lj_1
from q_NewInventory in lj_1.DefaultIfEmpty()
join Categories in db.Categorys on Inventory.CAT_ID equals Categories.CAT_ID into lj_2
from q_Categories in lj_2.DefaultIfEmpty()
join SCategories in db.StoneCategories on Inventory.TP_ID equals SCategories.TP_ID into lj_3
from q_SubCategory in lj_3.DefaultIfEmpty()
join qSupplier in db.Suppliers on Inventory.SUP_ID equals qSupplier.SUP_ID into lj_4
from q_Supplier in lj_4.DefaultIfEmpty()
join qStatus in db.Statuses on Inventory.ST_ID equals qStatus.ST_ID into lj_5
from q_Status in lj_5.DefaultIfEmpty()
join q_Locations in db.Locations on Inventory.LOC_ID equals q_Locations.LOC_ID into lj_7
from q_locations in lj_7.DefaultIfEmpty()
join q_Stone1 in db.Stones on Inventory.StoneID_1 equals q_Stone1.STONE_ID into lj_s1
from q_stone1 in lj_s1.DefaultIfEmpty()
join q_Stone2 in db.Stones on Inventory.StoneID_2 equals q_Stone2.STONE_ID into lj_s2
from q_stone2 in lj_s2.DefaultIfEmpty()
join q_Stone3 in db.Stones on Inventory.StoneID_3 equals q_Stone3.STONE_ID into lj_s3
from q_stone3 in lj_s3.DefaultIfEmpty()
join q_Stone4 in db.Stones on Inventory.StoneID_4 equals q_Stone4.STONE_ID into lj_s4
from q_stone4 in lj_s4.DefaultIfEmpty()
join q_Stone5 in db.Stones on Inventory.StoneID_5 equals q_Stone5.STONE_ID into lj_s5
from q_stone5 in lj_s5.DefaultIfEmpty()
join q_Stone6 in db.Stones on Inventory.StoneID_6 equals q_Stone6.STONE_ID into lj_s6
from q_stone6 in lj_s6.DefaultIfEmpty()
join q_Stone7 in db.Stones on Inventory.StoneID_7 equals q_Stone7.STONE_ID into lj_s7
from q_stone7 in lj_s7.DefaultIfEmpty()
join q_Stone8 in db.Stones on Inventory.StoneID_8 equals q_Stone8.STONE_ID into lj_s8
from q_stone8 in lj_s8.DefaultIfEmpty()
join qMasterInventory in db.MASTERINVENTORies on q_NewInventory.InvItemNo equals qMasterInventory.INVITEMNO into lj_6
from q_MasterInventory in lj_6.DefaultIfEmpty()
where Inventory.Barcode == _Barcode
select new
{
inv_InvID = Inventory.INV_ID, inv_Barcode = Inventory.Barcode,
inv_catID = Inventory.CAT_ID, inv_SubCatID = Inventory.TP_ID, inv_Price = Inventory.ITEM_PRICE, inv_Cost = Inventory.ITEM_COST,
inv_PricePoint = Inventory.PricePoint, inv_StatusID = Inventory.ST_ID, inv_StID = Inventory.ST_ID, inv_SupID = Inventory.SUP_ID,
inv_LocID = Inventory.LOC_ID, inv_LabSupplier = Inventory.LabSupplier, inv_LabStone1 = Inventory.LabStone1, inv_LabCategory = Inventory.LabCategory,
inv_LabExtra = Inventory.LabExtra, inv_LabMadeIn = Inventory.LabMadeIn, inv_Width = Inventory.ChainThickNess, inv_Size = Inventory.ChainSize,
inv_Stone1 = Inventory.StoneID_1,inv_Stone2 = Inventory.StoneID_2,inv_Stone3 = Inventory.StoneID_3,inv_Stone4 = Inventory.StoneID_4,inv_Stone5 = Inventory.StoneID_5,inv_Stone6 = Inventory.StoneID_6,inv_Stone7 = Inventory.StoneID_7,inv_Stone8 = Inventory.StoneID_8,inv_Stone9 = Inventory.StoneID_9,inv_Stone10 = Inventory.StoneID_10,
stat_Status = q_Status.DESCRIPTION,
cat_Category = q_Categories.DESCRIPTION,
subCat_SubCategory = q_SubCategory.DESCRIPTION,
sup_Supplier = q_Supplier.Name,
loc_Location = q_locations.DESCRIPTION,
mas_SKU = q_MasterInventory.INVITEMNO,
mas_GUID = q_MasterInventory.ItemGUID,
stone1 = q_stone1.DESCRIPTION,
stone2 = q_stone2.DESCRIPTION,
stone3 = q_stone3.DESCRIPTION,
stone4 = q_stone4.DESCRIPTION,
stone5 = q_stone5.DESCRIPTION,
stone6 = q_stone6.DESCRIPTION,
stone7 = q_stone7.DESCRIPTION,
stone8 = q_stone8.DESCRIPTION,
};
Create database view for this query and map the view to new read only entity. After that compare differences between pure LINQ execution and a new execution. If the problem was with converting your terrible LINQ query to SQL you will avoid it by using this approach. If it is still slow the problem will be elsewhere.
Guys in comments said that you have to re-write it in T-SQL. True. You can try one of SQL to LINQ converter tools.
Also use MS SQL Profiler to see what happens and improve TSQL performance.
Try to add indexes but not too much :)

Converting SQL to LINQ with INNER JOIN()?

I am struggling with how to write the below equivalent as LINQ. Truly I guess I am only struggling with how I represent the INNER JOIN () portion. Is that called a Nested Join? Anonymous Join? I am not even sure. Anyway, big thanks to anyone who can point me true. Even if it is just what this is called so I can BING it properly.
SELECT p.PersonID, p.FirstName, p.MiddleName, p.LastName, cp.EnrollmentID, cp.EnrollmentDate, cp.DisenrollmentDate
FROM vwPersonInfo AS p
INNER JOIN (
SELECT c.ClientID, c.EnrollmentID, c.EnrollmentDate, c.DisenrollmentDate
FROM tblCMOEnrollment AS c
LEFT OUTER JOIN tblWorkerHistory AS wh
ON c.EnrollmentID = wh.EnrollmentID
INNER JOIN tblStaffExtended AS se
ON wh.Worker = se.StaffID
WHERE (wh.EndDate IS NULL OR wh.EndDate >= getdate())
AND wh.Worker = --WorkerID Param Here
) AS cp
ON p.PersonID = cp.ClientID
ORDER BY p.PersonID
just put the inner query in its own variable. (It will be translated into one single SQL expression)
var innerQuery = from x in db.tblCMOEnrollment
where ...
select ...;
var query = from a in vwPersonInfo
join b innerQuery on p.PersonID equals cp.ClientID
select ...;
I think you can do this by writing a second method and joining on that method:
private static IEnumerable<Table> GetData(int joinKey)
{
return (from x in context.TableB.Where(id => id.Key == joinKey select x).AsQueryable();
}
Then you can do your normal query:
var query = from c in context.TableA
join GetData(c.PrimaryKeyValue)

How do I build up a LINQ => SQL / entities query (with joins) step-by-step?

I have the following two LINQ queries:
public int getJobsCount()
{
var numJobs =
(from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id
select j).Count();
return numJobs;
}
public List<Job> getJobs()
{
var jobs =
(
from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id
orderby j.issueDatetime descending
select new Job { x = j.field, y = c.field, etc }
).Skip(startJob - 1).Take(numJobs);
return jobs;
}
There's a lot of duplicate code in there - the "from", and "join" lines are identical, and I'll be adding in some "where" lines as well that will also be identical.
I tried adding a method that returned an IQueryable for the first part:
public IQueryable getJobsQuery()
{
var q =
from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id;
return q;
}
...but I get "a query body must end with a select clause or a group clause".
If I add a select clause on to the end off that function, I can't call count() on the result:
// getJobsQuery:
var q = from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id
select new { a = j.y, b = c.z }
// another method:
var q = getJobsQuery();
var numJobs = q.Count(); // "IQueryable doesn't contain a definition for count"
Is there a way to build up this query step-by-step to avoid duplicating a whole lot of code?
There are two ways of writing LINQ-queries, and though it doesn't really matter witch one you use it's good to know both of them cause they might learn you something about how LINQ works.
For instance, you have a set of jobs. If you were to select all jobs with an industryId of 5 (wild guess of data-types) you'd probably write something like this:
from j in dbConnection.jobs
where j.inustryId == 5
select j;
The very same query can also be written like this
dbConnections.jobs.Where(j => j.industryId == 5);
Now, I'm not here to preach saying one way is better than the other, but here you can clearly see how LINQ using the extension-methods syntax automatically selects on the iterated object (unless you do a select), whereas in the query-syntax you must do this explicitly. Also, if you were to add inn another where clause here it would look something like this:
from j in dbConnection.jobs
where j.inustryId == 5 // not using && here just to prove a point
where j.cityId == 3 // I THINK this is valid syntax, I don't really use the query-syntax in linq
select j;
While in the extension-methods you can just append more method-calls like so:
dbConnections.jobs.Where(j => j.industryId == 5)
.Where(j => j.cityId == 3);
Now this is good to know cause this means you can just put your linq-query inside a function an continue querying it. And all you need to do to make it work in your case is just explicitly select the starting variable j, or all the variables you need like so:
var q =
from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id;
select new {j = j, i = i, c = c, s = s, pt = pt };
return q;
Then you should be able to do for instance this:
getJobsQuery().Where(a => a.i.id == 5); // I used a as a name for "all", like the collection of variables
or using the query-syntax
from a in getJobsQuery()
where a.i.id == 5
select a;
Would this be better solved by returning a set of data (e.g. the common data) and querying for a subset of that data?
E.g. [pseudocode]
var allJobs =
(from j in dbConnection.jobs
join i in dbConnection.industries on j.industryId equals i.id
join c in dbConnection.cities on j.cityId equals c.id
join s in dbConnection.states on j.stateId equals s.id
join pt in dbConnection.positionTypes on j.positionTypeId equals pt.id
select j);
var myJobs = allJobs.OrderBy(j => j.issuedate).skip(expr).Take(allJobs.Count);
or similar...

Categories

Resources