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.
Related
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
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?
internal static IQueryable<TimeReviewDataModel> GetGridDetails(DiscoLlamaEntities context, string actor)
{
return (from t in context.TimeCaptures
join jc in context.JobCards on t.JobCardID equals jc.ID into jcSub
from jc in jcSub.DefaultIfEmpty()
join cu in context.Companies on jc.CustomerID equals cu.ID into cuSub
from tg in cuSub.DefaultIfEmpty()
where (t.CreatedBy == actor)
orderby t.Date descending
select new TimeReviewDataModel
{
ID = t.ID,
CustomerName = tg.Name,
Date = t.Date,
StartTime = t.StartTime,
EndTime = t.EndTime,
Description = t.Description,
Category = t.Category,
JobCardID = t.JobCardID,
VsoTask = t.VsoTaskID,
IsBillable = (bool)t.Billable
})
.OrderBy(e=>e.Date);
}
Hi, I'm trying to order my data in a grid. At the top I want the data that was entered today as the grid goes down I want the previously added entries. Currently it mixes the entries. I want to order using the Date property.
I'm using Linq and EntityFramework.
My Grid:
Try the following:
return (from t in context.TimeCaptures
join jc in context.JobCards on t.JobCardID equals jc.ID into jcSub
from jc in jcSub.DefaultIfEmpty()
join cu in context.Companies on jc.CustomerID equals cu.ID into cuSub
from tg in cuSub.DefaultIfEmpty()
where (t.CreatedBy == actor)
//orderby t.Date descending
select new TimeReviewDataModel
{
ID = t.ID,
CustomerName = tg.Name,
Date = t.Date,
StartTime = t.StartTime,
EndTime = t.EndTime,
Description = t.Description,
Category = t.Category,
JobCardID = t.JobCardID,
VsoTask = t.VsoTaskID,
IsBillable = (bool)t.Billable
})
.OrderByDescending(e=>e.Date).ThenByDescending(e=>eStartTime);
So, here you are ordering your final dataset, not an intermediate one.
I've had issues with my query that I was finally able to work through, but now duplicate date is formed even with Distinct(). I know these joins are messy, unfortunately it's what I have to do since the tables I'm working with have no relationships between them.
try
{
//work on query further , need to get client ID correctly
if (vehicleses.Count == 0)
return null;
string siteId = QueryExportSiteWithId(exportSiteId).SiteId;
// db.Database.Log = Console.Write;
var joinedInventorySettings = await (from id in db.Inventory_Descriptions
join iv in db.Inventory_Vehicles
on new {client = id.ClientID, id = id.InventoryID} equals new {client = iv.ClientID, id = iv.ID}
into descGroup
from m in descGroup.DefaultIfEmpty()
join se in db.Settings_Exports
on m.ClientID equals se.ClientID into settingGroup
from sg in settingGroup.DefaultIfEmpty()
join sl in db.Settings_Lots
on new {client = m.ClientID, id = m.LotID} equals new {client = sl.ClientID, id = sl.ID} into
lotsGroup
from lg in lotsGroup.DefaultIfEmpty()
join ses in db.Settings_ExportSites on new {client = m.ClientID, lotId = m.LotID, site = siteId}
equals new {client = ses.ClientID, lotId = ses.LotID, site = ses.Site} into exportGroup
from eg in exportGroup.DefaultIfEmpty()
join ifs in db.Inventory_Features
on new {client = m.ClientID, id = m.ID} equals new {client = ifs.ClientID, id = ifs.InventoryID}
into invFeatGroup
from ifg in invFeatGroup.DefaultIfEmpty()
join ip in db.Inventory_Photos
on m.ID equals ip.InventoryID into photo
from photos in photo.DefaultIfEmpty()
where m.Archived != "1"
&& m.Holding != "1"
&& m.Status == "A"
&& clientIdList.Contains(m.ClientID)
select new JoinedInventorySettings()
{
InventoryVehicles = m,
InventoryDescriptions = id,
SettingsExports = sg,
//InventoryPhotos = ,
SettingsLots = lg,
InventoryFeatures = ifg,
SettingsExportSites = eg
}).Distinct().ToListAsync();
if (joinedInventorySettings != null)
{
returnList.AddRange(joinedInventorySettings);
return returnList;
}
return null;
}
If anyone is curious, I was able to fix the issue by grouping items by two of the entity keys that I know will be in the dataset.
I have an Entity model with Invoices, AffiliateCommissions and AffiliateCommissionPayments.
Invoice to AffiliateCommission is a one to many, AffiliateCommission to AffiliateCommissionPayment is also a one to many
I am trying to make a query that will return All Invoices that HAVE a commission but not necessarily have a related commissionPayment. I want to show the invoices with commissions whether they have a commission payment or not.
Query looks something like:
using (var context = new MyEntitities())
{
var invoices = from i in context.Invoices
from ac in i.AffiliateCommissions
join acp in context.AffiliateCommissionPayments on ac.affiliateCommissionID equals acp.AffiliateCommission.affiliateCommissionID
where ac.Affiliate.affiliateID == affiliateID
select new
{
companyName = i.User.companyName,
userName = i.User.fullName,
email = i.User.emailAddress,
invoiceEndDate = i.invoicedUntilDate,
invoiceNumber = i.invoiceNumber,
invoiceAmount = i.netAmount,
commissionAmount = ac.amount,
datePaid = acp.paymentDate,
checkNumber = acp.checkNumber
};
return invoices.ToList();
}
This query above only returns items with an AffiliateCommissionPayment.
I'm not sure if EF supports this (nor am I sure if you are using EF2 or EF4), but this is the solution in Linq2Sql so it might be worth trying:
using (var context = new MyEntitities())
{
var invoices = from i in context.Invoices
from ac in i.AffiliateCommissions
join acp in context.AffiliateCommissionPayments on ac.affiliateCommissionID equals acp.AffiliateCommission.affiliateCommissionID into acp_join
from acp_join_default in acpg.DefaultIfEmpty()
where ac.Affiliate.affiliateID == affiliateID
select new
{
companyName = i.User.companyName,
userName = i.User.fullName,
email = i.User.emailAddress,
invoiceEndDate = i.invoicedUntilDate,
invoiceNumber = i.invoiceNumber,
invoiceAmount = i.netAmount,
commissionAmount = ac.amount,
datePaid = acp.paymentDate,
checkNumber = acp.checkNumber
};
return invoices.ToList();
}
The main change here is the into acpg after your join, and the DefaultIfEmpty line.
It's almost always a mistake to use join in LINQ to SQL and LINQ to Entities.
Guessing that the association from AffiliateCommission to AffiliateCommissionPayment is called Payment, you can just do:
using (var context = new MyEntitities())
{
var invoices = from i in context.Invoices
from ac in i.AffiliateCommissions
where ac.Affiliate.affiliateID == affiliateID
select new
{
companyName = i.User.companyName,
userName = i.User.fullName,
email = i.User.emailAddress,
invoiceEndDate = i.invoicedUntilDate,
invoiceNumber = i.invoiceNumber,
invoiceAmount = i.netAmount,
commissionAmount = ac.amount,
datePaid = (DateTime?) ac.Payment.paymentDate,
checkNumber = (int?) ac.Payment.checkNumber
};
return invoices.ToList();
}
LINQ to SQL and LINQ to Entities will both coalesce nulls. The casts are necessary because the inferred type will be based on the type of AffiliateCommissionPayment.paymentDate, which might not be nullable. If it is, you don't need the cast.