.Include in following query does not include really - entity-framework

var diaryEntries = (from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog")
.Include("DiaryEntryAction")
join diary in repository.GetQuery<OnlineDiary.Internal.Model.OnlineDiary>()
on entry.DiaryId equals diary.Id
group entry
by diary
into diaryEntriesGroup
select new { Diary = diaryEntriesGroup.Key,
DiaryEntry = diaryEntriesGroup.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault(),
});
This query does not include "DiaryEntryGradeChangeLog" and "DiaryEntryAction" navigation properties, what is wrong in this query?
I have removed join from the query and corrected as per below, and still it populates nothing
var diaryEntries = from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog").Include("DiaryEntryAction")
.Where(e => 1 == 1)
group entry
by entry.OnlineDiary
into diaryEntryGroups
select
new { DiaryEntry = diaryEntryGroups.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault() };

It will not. Include works only if the shape of the query does not change (by design). If you use this query it will work because the shape of the query is still same (OnlineDiary.Internal.Model.DiaryEntry):
var diaryEntries = (from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
.Include("DiaryEntryGradeChangeLog")
.Include("DiaryEntryAction");
But once you use manual join, grouping or projection (select new { }) you have changed the shape of the query and all Include calls are skipped.
Edit:
You must use something like this (untested) to get related data:
var diaryEntries = from entry in repository.GetQuery<OnlineDiary.Internal.Model.DiaryEntry>()
group entry by entry.OnlineDiary into diaryEntryGroups
let data = diaryEntryGroups.OrderByDescending(diaryEntry => diaryEntry.DateModified).FirstOrDefault()
select new {
DiaryEntry = data,
GradeChangeLog = data.DiaryEntryGradeChangeLog,
Action = data.DiaryEntryAction
};
or any similar query where you manually populate property for relation in projection to anonymous or unmapped type.

Related

Entity Framework, Linq : concatenating results from child table

I have an existing linq query which gets some data into a view model object. This is working fine.
I want to add a new property for data from child table which will have column values from a child table in a comma separated string format.
Problem: I am not able to concatenate the results using string.join
Simplified version of tables showing only relevant fields
part
id
part number
1
ABC1
2
DEF1
vendor
id
vendorname
1
acme
2
john
vendor part name (vendor specific part number)
partid
vendorid
partname
1
1
GDSE-553-32
1
2
JWWVV-HH-01
simplified version of query
result = (from p in DBContext.Parts.Where(w => w.EquipmentId == eId)
select new PartModel
{
Id = p.Id,
Number = p.PartNumber,
VendorPartNames= String.Join(",", DBContext.VendorPartName.Where(w => w.PartId == p.Id).Select(s => s.PartName))//this line causes exception (shown below)
});
Exception:
LINQ to Entities does not recognize the method 'System.String Join(System.String, System.String[])' method, and this method cannot be translated into a store expression.
Please note: the actual query has some joins and other columns, so please dont suggest solutions that requires joins.
If I change the "VendorPartName" to a List type , I can get the results without any problems.
My only problem is in "How to convert the results for "VendorPartName" property to a comma separated strings?"
eg: based on sample table data provided, it should be
GDSE-553-32, JWWVV-HH-01
Entity Framework does not support String.Join() method.
So, what we can do is to fetch VendorPartNames as a string collection and then we can later separate it with ,.
Note: For this, we would first use an anonymous object and later convert it to PartModel.
So your query would look like this:
var parts = DBContext.Parts
.Where(w => w.EquipmentId == eId)
.Select(p => new {
Id = p.Id,
Number = p.PartNumber,
VendorPartNames = p.VendorPartName.Select(n => n.PartName)
}).ToList();
var result = parts.Select(i => new PartModel {
Id = i.Id,
Number = i.Number,
VendorPartNames = String.Join(",", i.VendorPartNames)
}).ToList();

How to Data Fetch using Entity Framework in dotnet core

I have a table called "UserAnswers".below screenshot contains table data
I want to get data by surveyId and group by CreatedBy column.
for an example
There is a user called "amara#gmail.com".this user contains 4 records for a SurveyId.
I want to get this like below
Answers : [
{"2"},
{"1","0","1","1"},
{"1","2","4","3"},
{"Blue"}]
But my code returns this array for every rows.I meant duplicate records returning.
Here is my code
var qstns = await (from uans in _context.UserAnswers
where uans.SurveyId == id
select new UserAnswersReturnDto
{
UserEmail = uans.CreatedBy,
Qustns = (from ans in _context.UserAnswers
where ans.CreatedBy == uans.CreatedBy
select new UserAnswersSet
{
QNo = ans.QNo,
Ansrs = JsonConvert.DeserializeObject<JArray>(string.IsNullOrEmpty(ans.Answers) ? "[]" : ans.Answers)
}).ToArray()
}).ToListAsync();
So how to solve this issue.I opened many questions for this problem,but no one answered.Please help me.Thanks in advanced
You need to actually group your data before returning:
I used LINQ Lambda notation, but it should be quite easy to translate back to query if you're so inclined:
var qstns = _context.UserAnswers.Where(uans => uans.SurveyId == id)
.GroupBy(uans => uans.CreatedBy)
.Select(grans => new UserAnswersReturnDto {
UserEmail = grans.Key,
Qustions = grans.Select(ans => new UserAnswersSet() {
QNo = ans.QNo,
Ansrs = ans.Answers
}).ToList()
} ).ToList();
I didn't have time to double-check this, but I hope it serves as a guide to help you solve your issue!
There is no group by statement in your linq query.

How do I write a Linq query that needs a subquery?

I need to extract some information about support tickets from a database. Each ticket is associated with a medical imaging system, and each system may or may not have service cover associated with it. If it does, there may be multiple service cover entries, but only one that interests us.
I know this is not valid Linq, but what I would really like to do is the following...
var tickets = cxt.SupportTickets
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault(ci => // some query)
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});
Is there any way to do this? I know that I could repeat the t.CoverItems.FirstOrDefault(...) bit for every bit of data I want from the cover, but apart from the absolutely awful code mess this would produce, it would be very inefficient, as it would need to do the same subquery multiple times for every ticket.
I thought about breaking it all up into a foreach loop, but then I couldn't see how to create the tickets collection. I can't create an empty collection and then add objects to it, as they are anonymous types, and I wouldn't like to think about how you would specify the generic type!
Anyone any ideas?
You could improve readability:
var tickets = cxt.SupportTickets
.Select(t => new {
Ticket = t,
CoverItem = t.System.CoverItems.FirstOrDefault(ci => // some query)
})
.Select(x => new {
ID = x.Ticket.ID,
Customer = x.Ticket.Customer.Name,
CoverLevel = x.CoverItem?.Level.Name,
Expiry = x.CoverItem?.Expiry.ToLongDateString()
});
You could use query notation instead to use let clause:
var query=from t in cxt.SupportTickets
let cover = t.System.CoverItems.FirstOrDefault(ci => some query)
select new {
ID = t.ID,
Customer = t.Customer.Name,
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry//.ToLongDateString()
};
At the end is going to do the same that #TimSchmelter answer, but for things like that you can use let. Another thing, I'm almost sure ToLongDateString() method is not supported in EF.
I've tried this (If you wanted to develop a subQuery separately, because of SoC principle):
var innerQuery = cxt.SupportTickets
.Where(artist => artist.coverId == SomeParameter)
.Select(artist => new {
artistId = artist.artistId,
artistCompleteName = artist.artistName,
artistMasterPiece = artist.CoverName
});
var tickets = cxt.SupportTickets
.Where(
t => innerQuery.Contains(t.coverId)
)
.Select( t => new {
ID = t.ID,
Customer = t.Customer.Name,
var cover = t.System.CoverItems.FirstOrDefault()
CoverLevel = cover?.Level.Name,
Expiry = cover?.Expiry.ToLongDateString()
});

EF Left joining a table on two properties combined with a case statement

I'm trying to write a query for a database that will left join a table to a look up table and the results will be returned based on a case statement.
In normal SQL the query would look like this:
SELECT chis_id, chis_detail, cilt.mhcatID, cilt.mhtID, 'TheFileName' =
CASE
WHEN cilt.mhcatID IS NOT NULL AND cilt.mhtID IS NOT NULL THEN chis_linked_filename
END
FROM chis
LEFT JOIN cilt on cilt.mhcatID = chis.mhcat_id AND cilt.mhtID = chis.mht_id
WHERE cch_id = 50
chis is the table being queried, cilt is a look-up table and does not contain any foreign key relationships to chis as a result (chis has existing FK's to mht and mhcat tables by the mhtID and mhcatID respectively).
The query will be used to return a list of history updates for a record. If the join to the cilt lookup table is successful this means that the caller of the query will have permission to view the filename of any associated files for the history updates.
Whilst during my research I've found various posts on here relating on how to do case statements and left joins in Linq to Entity queries, I've not been able to work out how to join on two different fields. Is this possible?
You need to join on an anonymous type with matching field names like so:
var query = from x in context.Table1
join y in context.Table2
on new { x.Field1, x.Field2 } equals new { y.Field1, y.Field2 }
select {...};
A full working example using the an extra from instead of a join would look something like this:
var query = from chis in context.Chis
from clit in context.Clit
.Where(x => x.mhcatID = chis.mhcat_id)
.Where(x => x.mhtID = chis.mht_id)
.DefaultIfEmpty()
select new
{
chis.id,
chis.detail,
cilt.mhcatID,
cilt.mhtID,
TheFileName = (cilt.mhcatID != null && cilt.mhtID != null) ? chis.linked_filename : null
};
Based on what Aducci suggested, I used a group join and DefaultIsEmpty() to get the results I wanted. For some reason, I couldn't get DefaultIfEmpty() didn't work correctly on its own and the resulting SQL employed an inner join instead of a left.
Here's the final code I used to get the left join working:
var query = (from chis in context.chis
join cilt in context.cilts on new { MHT = chis.mht_id, MHTCAT = chis.mhcat_id } equals new { MHT = cilt.mhtID, MHTCAT = cilt.mhcatID } into tempCilts
from tempCilt in tempCilts.DefaultIfEmpty()
where chis.cch_id == 50
select new {
chisID = chis.chis_id,
detail = chis.chis_detail,
filename = chis.chis_linked_filename,
TheFileName = (tempCilt.mhcatID != null && tempCilt.mhtID != null ? chis.chis_linked_filename : null),
mhtID = chis.mht_id,
mhtcatID = chis.mhcat_id
}).ToList();

Converting T-SQL to Linq

I am using Entitry Framework 4.1 and I am struggling to understand how the convert the below query which uses joins and aggregate methods to a Linq to Entities call in the DomainService.
SELECT tblTime.Period As Timeline, COUNT(tblEngineeringDashboard_ItemList.ID) AS Items
FROM tblEngineeringDashboard_ItemList INNER JOIN
tblTime ON tblEngineeringDashboard_ItemList.TimeID = tblTime.ID
GROUP BY tblTime.Period
ORDER BY tblTime.Period
Can anyone provide help.
Possible Solution
Dim var = From i In ObjectContext.tblEngineeringDashboard_ItemList
Join t In ObjectContext.tblTimes On i.TimeID Equals t.ID
Group By i.TimeID Into Group
Select DateStart = (From n In ObjectContext.tblTimes Where n.ID = TimeID Select n.Period), PartCount = Group.Count
Phil
The first thing which comes to mind is:
var q = from t in Context.Time
group t by t.Period into g
orderby g.Key
select new
{
Timeline = g.Key,
Items = (from ti in g
from il in ti.ItemList // or whatever the property for the navigation to tblEngineeringDashboard_ItemList is called
select il).Count()
};
However, the original SQL had an INNER JOIN, which would reject tblTime records without any matching records in tblEngineeringDashboard_ItemList. So you may want:
var q = from t in Context.Time
where t.ItemList.Any()
group t by t.Period into g
orderby g.Key
select new
{
Timeline = g.Key,
Items = (from ti in g
from il in ti.ItemList // or whatever the property for the navigation to tblEngineeringDashboard_ItemList is called
select il).Count()
};
You can also flip the query around:
var q = from i in Context.EngineeringDashboardItemList
where i.Time != null
group i by i.Time.Period into g
orderby g.Key
select new
{
Timeline = g.Key,
Items = g.Count()
};
Does this work?
tblEngineeringDashboard_ItemList
.Join(tblTime,ed => ed.TimeID ,t => t.ID, (ed,t) => new{ed,t})
.GoupBy(g => g.t.Period)
.Select(s => new
{
Timeline = s.Key,
Items = s.Count()
}
)
.OrderBy(o => o.Timeline)
While converting from sql to linq isn't an ideal approach (you should think directly in linq, translating your need to a linq query), the query you posted is rather simple.
var grouped = tblTime.OrderBy(c => c.Period).GroupBy(c => c.Period).Select(c =>
new {
timeline = c.Key,
count = c.SelectMany(x => x.tblEngineeringDashboard).Count()
});
*Edit: There, fixed. Everything on the L2E engine.
This provided that there are correct foreign keys between the tables (thus you don't have to declare the join manually).