Linq group by on multiple table and inner join - c#

I have SQL query and i would like to rewrite it by linq. This sql command only joins two table and do group by on them. Problem is in group by. When i use group by on one table, all is ok and linq commant returns the same result as sql command. But when i want to join two table and then group by H.Ucet (for example) then it returns other result as my sql command. The result is the same as when using a Left Join, but i want default inner join.
This is sql command:
string dotazBankUcty = #"SELECT
H.UCET,
SUM(H.MD) AS MD,
SUM(H.DAL) AS DAL ,
SUM(H.MD_M) AS MD_M,
SUM(H.DAL_ENA) DAL_M,
MAX(UBUC.KOD) AS KOD
FROM ACCOUNT H
inner join UBU on H.UCET = UBU.UCET GROUP BY H.UCET";
I try to rewrite it by this linq command.
var accountQuery = new XPQuery<ACCOUNT >(CoreHelper.DataSession);
var ubuQuery = new XPQuery<UBU>(CoreHelper.DataSession);
var resultBankyUcty = (from h in accountQuery
join u in ubuQuery on h.CompoundKey1.UCET equals u.UCET
group new { h, u } by new { h.CompoundKey1.UCET } into gUcty
select new
{
Ucet = gUcty.Key.UCET,
MD = gUcty.Sum(k => k.h.MD),
DAL = gUcty.Sum(k => k.h.DAL),
MD_M = gUcty.Sum(k => k.h.MDM),
DAL_M = gUcty.Sum(k => k.h.DALM),
KOD = gUcty.Max(k => k.u.KOD)
});
Can anyone help me ? I can't see an error.

from t in (
from h in accountquery
join u in ubuQuery on h.CompoundKey1.UCET equals u.UCET
select new {h,u}
) group t by new {t.h.CompoundKey1.UCET} into g
select new {
Ucet = g.Key.UCET,
MD = g.Sum(k => k.h.MD),
DAL = g.Sum(k => k.h.DAL),
MD_M = g.Sum(k => k.h.MDM),
DAL_M = g.Sum(k => k.h.DALM),
KOD = g.Max(k => k.u.KOD)
};

Related

How can I use Linq to include a List<myDto> in selectTableDto?

I am currently using Linq to join two tables together, mainTable and selectTable, they are joined on mainTable.ID = selectTable.mtID. I am trying to include a third table, myTable, that is joined on selectTable.ID = myTable.selID. There will be many records in myTable for one ID from selectTable so I'm trying to get List<myTable>. This is what I have so far that works:
public async Task<List<mainTableDto>> listAll()
{
var db = _repository.DbContext;
var result = await ( from mt in db.mainTable
join sel in db.selectTable
on mt.ID equals sel.mtID
select new mainTableDto
{
ID = mt.ID,
createDate = mt.createDate,
selectTable = new selectTableDto
{
ID = sel.ID
name = sel.name
}
}
}).ToListAsync;
return result;
I've tested getting data from selectTableDto with List< myTableDto> and it works.
I'm a little stuck on how to include a List<myTableDto> into this nested call. I've tried:
join sel in db.selectTableInclude(x=>x.myTableDto)
But when I do this I don't get the info from myTableDto and just get null instead (I've put data in the DB so there should be something)
I've also tried:
join sel in db.selectTable
on mt.ID equals sel.mtID
join my in db.myTable
on sel.ID equals my.selID
selectTable = new selectTableDto
{
ID = sel.ID
name = sel.name
myTableDto = new List<myTableDto>
{
ID = my.ID
}
}
But when I do this it says "ID is not a member of myTableDTO".
Any advice on what I'm doing wrong?
I believe you want a groupjoin (method syntax) or into (query syntax)
This is query syntax:
from mt in db.mainTable
join sel in db.selectTable
on mt.ID equals sel.mtID
into mainTableSels
select new mainTableDto
{
ID = mt.ID,
createDate = mt.createDate,
selectTable = from mts in mainTableSels select new selectTableDto
{
ID = mts.ID
name = mts.name
}
}
Though I do personally prefer a hybrid query/method syntax:
from mt in db.mainTable
join sel in db.selectTable
on mt.ID equals sel.mtID
into mainTableSels
select new mainTableDto
{
ID = mt.ID,
createDate = mt.createDate,
selectTable = mainTableSels.Select(mts => new selectTableDto
{
ID = mts.ID
name = mts.name
})
}
I'm not clear on what type your mainTableDto.selectTable property is; if it's an array or list you'll need a ToArray/ToList. If it's IEnumerable then it should work without

Linq: Join vs Include/ThenInclude in Entity Framework Core

I am working on a project with SQL Server and EF Core v3.
I have 4 tables related to each other. Here are my tables schemes:
I wrote 2 Linq queries against those tables - one of them using join like this:
var result = (from emailTemplate in _context.EmailTemplates.Include(et => et.EmailTemplateContents)
join priorityLookup in _context.Lookups on new { GroupKey = "PRIORITIES", DetailKey = emailTemplate.Priority, emailTemplate.CompanyId } equals new { priorityLookup.GroupKey, priorityLookup.DetailKey, priorityLookup.CompanyId }
join statusLookup in _context.Lookups on new { GroupKey = "STATUSES", DetailKey = emailTemplate.StatusCode, emailTemplate.CompanyId } equals new { statusLookup.GroupKey, statusLookup.DetailKey, statusLookup.CompanyId }
join priorityLookupLabel in _context.LookupLabels on new { Locale = 1033, priorityLookup.DetailKey, priorityLookup.CompanyId } equals new { priorityLookupLabel.Locale, priorityLookupLabel.DetailKey, priorityLookupLabel.CompanyId }
join statusLookupLabel in _context.LookupLabels on new { Locale = 1033, statusLookup.DetailKey, statusLookup.CompanyId } equals new { statusLookupLabel.Locale, statusLookupLabel.DetailKey, statusLookupLabel.CompanyId }
where emailTemplate.CompanyId == 3
select new EmailTemplateModel
{
Code = emailTemplate.Code,
TemplateName = emailTemplate.TemplateName,
FromEmail = emailTemplate.FromEmail,
BccEmail = emailTemplate.BccEmail,
CcEmail = emailTemplate.CcEmail,
PriorityCode = emailTemplate.Priority,
Priority = priorityLookupLabel.Label,
Subject = emailTemplate.EmailTemplateContents.Subject,
Body = HttpUtility.HtmlDecode(emailTemplate.EmailTemplateContents.Body),
StatusCode = emailTemplate.StatusCode,
Status = statusLookupLabel.Label,
ToEmail = emailTemplate.ToEmail,
TriggerSqlCommand = emailTemplate.TriggerSqlCommand,
TriggerType = emailTemplate.TriggerType,
ModifDate = emailTemplate.ModifDate
}).ToList();
and one of them using .Include like so :
var results = _context.EmailTemplates
.Where(e => e.CompanyId == 3)
.Include(e => e.EmailTemplateContents)
.Include(e => e.Lookups)
.ThenInclude(g => g.LookupLabels)
.Include(e => e.LookupsNavigation)
.ThenInclude(g => g.LookupLabels)
.Select(e => new EmailTemplateModel
{
Code = e.Code,
TemplateName = e.TemplateName,
FromEmail = e.FromEmail,
BccEmail = e.BccEmail,
CcEmail = e.CcEmail,
PriorityCode = e.Priority,
PriorityLabel = e.Lookups.LookupLabels.FirstOrDefault(l => l.Locale == 1033),
Subject = e.EmailTemplateContents.Subject,
Body = HttpUtility.HtmlDecode(e.EmailTemplateContents.Body),
StatusCode = e.StatusCode,
StatusLabel = e.LookupsNavigation.LookupLabels.FirstOrDefault(l => l.Locale == 1033),
ToEmail = e.ToEmail,
TriggerSqlCommand = e.TriggerSqlCommand,
TriggerType = e.TriggerType,
ModifDate = e.ModifDate
}).ToList();
I tried to understand if there is any performance difference between these two type, so I checked the query generated by EF using profiler.
The SQL Script generated from the Join statement is :
SELECT
[e].[Code], [e].[TemplateName], [e].[FromEmail], [e].[BccEmail],
[e].[CcEmail], [e].[Priority], [l1].[Label],
[e0].[Subject], [e0].[Body], [e].[StatusCode], [l2].[Label],
[e].[ToEmail], [e].[TriggerSqlCommand], [e].[TriggerType],
[e].[ModifDate]
FROM [EmailTemplates] AS [e]
INNER JOIN [Lookups] AS [l] ON (('PRIORITIES' = [l].[GroupKey]) AND ([e].[Priority] = [l].[DetailKey])) AND ([e].[CompanyId] = [l].[CompanyId])
INNER JOIN [Lookups] AS [l0] ON (('STATUSES' = [l0].[GroupKey]) AND ([e].[StatusCode] = [l0].[DetailKey])) AND ([e].[CompanyId] = [l0].[CompanyId])
INNER JOIN [LookupLabels] AS [l1] ON ((1033 = [l1].[Locale]) AND ([l].[DetailKey] = [l1].[DetailKey])) AND ([l].[CompanyId] = [l1].[CompanyId])
INNER JOIN [LookupLabels] AS [l2] ON ((1033 = [l2].[Locale]) AND ([l0].[DetailKey] = [l2].[DetailKey])) AND ([l0].[CompanyId] = [l2].[CompanyId])
LEFT JOIN [EmailTemplateContents] AS [e0] ON ([e].[CompanyId] = [e0].[CompanyId]) AND ([e].[Code] = [e0].[EmailTemplateCode])
WHERE [e].[CompanyId] = 3
The SQL script generated from the .Include statement is :
SELECT
[e].[Code], [e].[TemplateName], [e].[FromEmail], [e].[BccEmail],
[e].[CcEmail], [e].[Priority], [t0].[DetailKey], [t0].[CompanyId],
[t0].[Locale], [t0].[Label], [t0].[OrderNo], [e0].[Subject], [e0].[Body],
[e].[StatusCode], [t2].[DetailKey], [t2].[CompanyId], [t2].[Locale],
[t2].[Label], [t2].[OrderNo], [e].[ToEmail], [e].[TriggerSqlCommand],
[e].[TriggerType], [e].[ModifDate]
FROM [EmailTemplates] AS [e]
INNER JOIN [Lookups] AS [l] ON ([e].[Priority] = [l].[DetailKey]) AND ([e].[CompanyId] = [l].[CompanyId])
LEFT JOIN [EmailTemplateContents] AS [e0] ON ([e].[CompanyId] = [e0].[CompanyId]) AND ([e].[Code] = [e0].[EmailTemplateCode])
INNER JOIN [Lookups] AS [l0] ON ([e].[StatusCode] = [l0].[DetailKey]) AND ([e].[CompanyId] = [l0].[CompanyId])
LEFT JOIN (
SELECT [t].[DetailKey], [t].[CompanyId], [t].[Locale], [t].[Label], [t].[OrderNo]
FROM (
SELECT [l1].[DetailKey], [l1].[CompanyId], [l1].[Locale], [l1].[Label], [l1].[OrderNo], ROW_NUMBER() OVER(PARTITION BY [l1].[DetailKey], [l1].[CompanyId] ORDER BY [l1].[DetailKey], [l1].[CompanyId], [l1].[Locale]) AS [row]
FROM [LookupLabels] AS [l1]
WHERE [l1].[Locale] = 1033
) AS [t]
WHERE [t].[row] <= 1
) AS [t0] ON ([l].[DetailKey] = [t0].[DetailKey]) AND ([l].[CompanyId] = [t0].[CompanyId])
LEFT JOIN (
SELECT [t1].[DetailKey], [t1].[CompanyId], [t1].[Locale], [t1].[Label], [t1].[OrderNo]
FROM (
SELECT [l2].[DetailKey], [l2].[CompanyId], [l2].[Locale], [l2].[Label], [l2].[OrderNo], ROW_NUMBER() OVER(PARTITION BY [l2].[DetailKey], [l2].[CompanyId] ORDER BY [l2].[DetailKey], [l2].[CompanyId], [l2].[Locale]) AS [row]
FROM [LookupLabels] AS [l2]
WHERE [l2].[Locale] = 1033
) AS [t1]
WHERE [t1].[row] <= 1
) AS [t2] ON ([l0].[DetailKey] = [t2].[DetailKey]) AND ([l0].[CompanyId] = [t2].[CompanyId])
WHERE [e].[CompanyId] = 3
I compared the actual execution plans for both to see what the difference between these two is.
Here is the join execution plan:
and here is the include execution plan:
The cost of both queries are the same 50%.
Now I have couple of questions :
Based on the query cost(50%) should I consider these two equal performance-wise ?
Is there any suggestion for using include or join to make one of them faster or with fewer cost?
What are the pros and cons of using Join (Syntax/maintenance)?
What are the pros and cons of using Include (Syntax/maintenance)?
Which one should I use if the table has a few records or if it has lots of records?

Linq, Json, Select product from db

I have a 3 tables Dish, Wine, Suggestion.
Then the idea is use table suggestion table to put the dish and the wine making one of them suggestion each other.
I'm using LINQ, but when one product doesn't have a suggestion he does not add to the json.
var query = (from m in db.Dish
join t in db.TypeDish on m.idTypeDish equals t.idTypeDish
join i in db.ImageDish on m.idDish equals i.idDish into g
join s in db.Suggestion on m.idDish equals s.idDish
join si in db.ImageWine on s.idWine equals si.idWine into f
where m.idTypeDish == dish
select new DishModel()
{
Name = m.name,
CalorificValue = m.calorificValue,
Price = m.price,
ShortName = m.shortName,
Time = m.manufactureTime,
Description = m.description,
UrlImageList = g.Select(i => _url + i.Image.urlImage).ToList(),
BeveragesList = new List<BeverageModel>()
{
new BeverageModel()
{
Name = s.Wine.name,
ShortName = s.Wine.shortName,
Price = s.Wine.price,
Description = s.Wine.description,
AlcoholContent = s.Wine.alcoholContent,
WineEnum = WineEnum.WhiteWine,
Region = s.Wine.Region.name,
WineCaste = s.Wine.wineCaste,
UrlImageList = f.Select(i => _url+i.Image.urlImage).ToList(),
}
}
}).ToList();
return query;
Now I have 2 items on DB, and he sends only one because the other don't have a suggestion.
The error is on joins probably, but I'm a newbie in Linq.
Thanks.

The type of one of the expressions in the join clause is incorrect in Entity Framework. Constant in left join

I'm trying to do a left join in an EF query. I'm getting the following error:
Error CS1941 The type of one of the expressions in the join clause is
incorrect. Type inference failed in the call to 'GroupJoin'
and here is the C# code:
var foo = from m in db.ClientMasters
join a in db.Orders on new { m.Id, Status = "N" } equals new { a.ClientID, a.Status } into a_join
from a in a_join.DefaultIfEmpty()
select new { m.ClientID, a.ID };
The column names have to match in the join; here is the corrected code:
var foo = from m in db.ClientMasters
join a in db.Orders on new { ClientID = m.Id, Status = "N" } equals new { a.ClientID, a.Status } into a_join
from a in a_join.DefaultIfEmpty()
select new { ClientID = m.Id, OrderId = a.Id };

Linq to SQL with Group by

I am trying to convert this T-SQL to a Linq To SQL but can't work out the group by aggregate functions. Any help welcome.
select c.ClientID, GivenName, Surname, max(a.Address), max(t.Value)
from Client c
left join ClientAddress a on c.ClientID = a.ClientID
left join ClientContact t on c.ClientID = t.ClientID
group by c.ClientID, GivenName, Surname
To group by a composite key, you typically use an anonymous type:
var qry = from x in someSource
group x by new { x.ClientID, x.GivenName, x.Surname } into grp
select new { grp.Key, Address = grp.Max(x => x.Address),
Value = grp.Max(x => x.Value) };
The exact answer I came up with was
public IQueryable<ClientSearchDTO> GetClientsDTO()
{
return (from client in this.Context.Clients
join address in this.Context.ClientAddresses on client.ClientID equals address.ClientID
join contact in this.Context.ClientContacts on client.ClientID equals contact.ClientID
where contact.ContactType == "Phone"
group client by new { client.ClientID, client.Surname, client.GivenName } into clientGroup
select new ClientSearchDTO()
{
ClientID = clientGroup.Key.ClientID,
Surname = clientGroup.Key.Surname,
GivenName = clientGroup.Key.GivenName,
Address = clientGroup.Max(x => x.ClientAddresses.FirstOrDefault().Address),
PhoneNumber = clientGroup.Max(x => x.ClientContacts.FirstOrDefault().Value)
});
}

Categories