How to use LINQ with calculated properties in a single pass? - c#

I'd like to make a LINQ query, extracting dynamic properties (calculated fields) of my entities in a single pass, without get the error "The specified type member 'EntityKey' is not supported in LINQ to Entities".
Here is the only working way I found, but I am sure there are better and more elegant methods:
var q = (from i in
(from x in context.Tickets
select new { x.OperatoreID, x.DataObiettivo })
group i by new { i.OperatoreID } into g
select new vmOperatoreDateObiettivo
{
OperatoreID = g.Key.OperatoreID,
NOperatore = "", // field value to be updated...
DataObiettivo = g.Max(d => d.DataObiettivo),
MinutiAllaScadenza = 0, // field to be updated...
Alert = "" // field value to be updated...
}).ToList();
// Here I update my fields with a second pass....
foreach (vmOperatoreDateObiettivo e in q)
{
string nome = context.Operatori
.Where(t => t.OperatoreID == e.OperatoreID)
.First().CognomeNomePuntato.ToString();
e.NOperatore = nome;
int minscad = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID).AsEnumerable().Min(a => a.MinutiAllaScadenza);
e.MinutiAllaScadenza = minscad;
string sev = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID).AsEnumerable().Min(a => a.Alert);
e.Alert = sev;
}
Thanks in advance!

Try adding a let clause to your query and define calculated field, like so:
var q = (from i in
(from x in context.Tickets
select new { x.OperatoreID, x.DataObiettivo })
group i by new { i.OperatoreID } into g
let nOperatore = context.Operatori
.Where(t => t.OperatoreID == e.OperatoreID)
.First().CognomeNomePuntato.ToString() &&
minutialla = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID)
.AsEnumerable().Min(a => a.MinutiAllaScadenza) &&
alert = context.Tickets
.Where(t => t.OperatoreID == e.OperatoreID)
.AsEnumerable().Min(a => a.Alert)
select new vmOperatoreDateObiettivo
{
OperatoreID = g.Key.OperatoreID,
NOperatore = nOperatore,
DataObiettivo = g.Max(d => d.DataObiettivo),
MinutiAllaScadenza = minutialla,
Alert = alert
}).ToList();

Related

C# Linq compress join query with where clause

Hi I am using below code to fetch required data from 2 tables using linq syntax which is working fine.
var ratings = from r in _ratingRepository.AsQueryable()
join c in _convRepository.AsQueryable()
on r.SessionId equals c.CurrentConversationSid
where!c.IsDeleted && c.DateCreated >= request.From && c.DateCreated <=
request.To && c.HasRated
select new Rating() {
Id = r.Id,
SessionId = r.SessionId,
Questions = r.Questions,
AvgRatingValue = r.AvgRatingValue
};
I want to transform this code using below syntax
IQueryable<Rating> ratingsObj = _ratingRepository.AsQueryable()
.Join(_convRepository.AsQueryable().Where(a => a.HasRated), r => r.SessionId, c => c.CurrentConversationSid, (r, c) =>
new Rating()
{
Id = r.Id,
SessionId = r.SessionId,
Questions = r.Questions,
AvgRatingValue = r.AvgRatingValue
});
Its gives below error
System.ArgumentException: 'Expression of type
'System.Collections.Generic.IEnumerable1[Flecx.Chat.Entities.Conversation]' cannot be used for parameter of type 'System.Linq.IQueryable1[Flecx.Chat.Entities.Conversation]' of method
'System.Linq.IQueryable1[Flecx.Chat.Entities.Conversation] Where[Conversation](System.Linq.IQueryable1[Flecx.Chat.Entities.Conversation],
System.Linq.Expressions.Expression1[System.Func2[Flecx.Chat.Entities.Conversation,System.Boolean]])'
(Parameter 'arg0')'
If I remove this code .Where(a => a.HasRated) it runs fine. How can I include the where clause in above syntax.
Need help
try this:
var ratingsObj = _ratingRepository.AsQueryable()
.Join(_convRepository.AsQueryable(),
r => r.SessionId,
c => c.CurrentConversationSid,
(r,c)=>new {r,c}) //**
.Where(a => a.c.HasRated)
.Select(x => new Rating()
{
Id = x.r.Id,
SessionId = x.r.SessionId,
Questions = x.r.Questions,
AvgRatingValue = x.r.AvgRatingValue
});
you can filter anything you want in line with '//**' same below:
(r, c) => new
{ r.Id,
r.SessionId,
r.Questions,
r.AvgRatingValue,
c.HasRated
}
then your code is changed to this:
var ratingsObj = _ratingRepository.AsQueryable()
.Join(_convRepository.AsQueryable(),
r => r.SessionId,
c => c.CurrentConversationSid,
(r, c) => new
{ r.Id,
r.SessionId,
r.Questions,
r.AvgRatingValue,
c.HasRated})
.Where(a => a.HasRated)
.Select(x => new Rating()
{
Id = x.Id,
SessionId = x.SessionId,
Questions = x.Questions,
AvgRatingValue = x.AvgRatingValue
});

Aggregate of string on anonymous type

I want to concat multiple string value into single string with comma separated,i tried using aggregate function but it shows cannot convert string to how to fix this issue,
I tried below code
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = ((from st in WYNKContext.SurgeryTran.
Where(x => x.SurgeryID == e.SurgeryID)
select new
{
desc = icdmaster
.Where(x => x.ID ==
st.IcdSpecialityCode).Select(x =>
x.SpecialityDescription).FirstOrDefault(),
}).ToList()).Aggregate((a, b) => a.desc + "," + b.desc),
}).ToList();
I want Output like inside surgery property like = string1,string 2 ,etc....
without using aggregate i am getting as count in Surgery Property
var res = (from e in WYNKContext.SurgeryAssigned.Where(x => x.CmpID == cmpid && x.IsCancelled == false)
select new
{
ID = e.SAID,
UIN = e.UIN,
SurgeryDate = e.SurgeryDate,
SurgeryID = e.SurgeryID,
Surgery = (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList(),
}).ToList();
also tried string join :
Surgery = string.Join(",", (from st in WYNKContext.SurgeryTran.Where(x => x.SurgeryID == e.SurgeryID)
select new
{
icd = icdmaster.Where(x => x.ID == st.IcdSpecialityCode).Select(x => x.SpecialityDescription).FirstOrDefault(),
}).ToList()),
but in output i am getting like this
Surgery ={ icd = CORNEA },{ icd = CATARACT/IOL }
can some one tell what i did wrong in string.join.....
The string class has a static method named Join, which takes in a collection of items and a string to join them with, which should work for you here.
If I'm reading your code correctly, it would look something like this:
Surgery = string.Join(",", WYNKContext.SurgeryTran
.Where(surgTran => surgTran.SurgeryID == e.SurgeryID)
.Select(surgTran => icdmaster
.Where(icd => icd.ID == surgTran.IcdSpecialityCode)
.Select(icd => icd.SpecialityDescription)
.FirstOrDefault())),

DefaultIfEmpty() does not handle empty collections

I've been trying to left join the table and they are in a one-to-many relationship.
I have written a SQL query and trying to convert it into LINQ for my ASP.NET Core application.
My sql query is as follows:
SELECT ap.SystemId,
ap.AccessRequiredToId,
cb.AccessAreaManagementId,
ap.EquipmentTagId,
COUNT(ap.Name) [Count]
FROM ApplicationForms ap LEFT JOIN AccessAreaCheckBoxes cb
ON n ap.RecordId = cb.RecordId
WHERE EndDate IS NULL AND (Checked IS NULL OR Checked = 1)
GROUP BY ap.SystemId, ap.AccessRequiredToId, cb.AccessAreaManagementId, ap.EquipmentTagId
SQL Result
And my LINQ is as follows:
var active = _context.ApplicationForms
.Where(w => w.EndDate == null)
.GroupJoin(_context.AccessAreaCheckBoxes
.Where(w => (w.AccessAreaManagement == null || w.Checked == true)),
x => x.RecordId,
y => y.RecordId,
(x, y) => new { ApplicationForms = x, AccessAreaCheckBoxes = y })
.SelectMany(x => x.AccessAreaCheckBoxes.DefaultIfEmpty(),
(x, y) => new { x.ApplicationForms, AccessAreaCheckBoxes = y })
.GroupBy(g => new { g.ApplicationForms.System, g.ApplicationForms.AccessRequiredTo, g.AccessAreaCheckBoxes.AccessAreaManagement, g.ApplicationForms.EquipmentTag })
.Select(s => new RecordViewModel
{
System = s.Key.System.Name,
AccessRequiredTo = s.Key.AccessRequiredTo.Name,
AccessArea = s.Key.AccessAreaManagement.Name,
EquipmentTag = s.Key.EquipmentTag.Name,
Count = s.Count()
}).ToList();
Everything is working well except it doesn't show the rows with the NULL value.
Did I miss out something in my LINQ?
Any help would be greatly appreciated!
This is what I do in the end, post here for your reference.
var active = (from ap in _context.ApplicationForms
join cb in _context.AccessAreaCheckBoxes
on ap.RecordId equals cb.RecordId into j1
from j2 in j1.DefaultIfEmpty()
where ap.EndDate == null
&& (j2.AccessAreaManagement == null || j2.Checked == true)
group new { ap.System, ap.AccessRequiredTo, j2.AccessAreaManagement, ap.EquipmentTag }
by new { System = ap.System.Name, Building = ap.AccessRequiredTo.Name, AccessArea = j2.AccessAreaManagement.Name, Equipment = ap.EquipmentTag.Name } into grp
select new RecordViewModel
{
System = grp.Key.System,
AccessRequiredTo = grp.Key.Building,
AccessArea = grp.Key.AccessArea,
EquipmentTag = grp.Key.Equipment,
Count = grp.Count()
}).ToList();

How to remove specefic rows from linq result

I have this Linq to Entity:
var sitesGrpByClientAndFreq = from cfr in clientFullReview2
group cfr by new { cfr.inspectionFreqvency } into g
select new
{
inspectionFreqvency = g.Key.inspectionFreqvency,
siteCount = g.Select(x => x.siteId).ToArray().Distinct().Count(),
normalSitesCount = g.Where(x => x.isNormal == true).Select(x=>x.isNormal).ToArray().Count(),
}
From the above linq I get this result:
My question is How can I remove all rows where inspectionFrequency = -1?
You can mix and match the let keyword with where. It could be something like:
var sitesGrpByClientAndFreq =
from cfr in clientFullReview2
group cfr by new { cfr.inspectionFreqvency } into g
let inspectionFreqvency = g.Key.inspectionFreqvency
where inspectionFreqvency != -1
select new
{
inspectionFreqvency = inspectionFreqvency,
siteCount = g.Select(x => x.siteId).ToArray().Distinct().Count(),
normalSitesCount = g.Where(x => x.isNormal == true).Select(x=>x.isNormal).ToArray().Count(),
}

linq after groupby unable to get column values

I am getting data from multiple tables by joining and i want to group data on particular column value but after group by statement i can access my aliases and their properties. What mistake i am making?
public List<PatientHistory> GetPatientHistory(long prid)
{
using(var db = new bc_limsEntities())
{
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = r.resultid,
bookingid = r.bookingid,
testid = r.testid,
prid = r.prid,
attributeid = r.attributeid,
result = r.result,
Test_Name = t.Test_Name,
Attribute_Name = a.Attribute_Name,
enteredon = r.enteredon,
Attribute_Type = a.Attribute_Type
}).ToList();
return result;
}
}
You're doing this wrong way. As been said by Jon after grouping the sequences with aliases r,t,a doesn't exist. After grouping you receive the sequence g with sequances of r,t,a in each element of g. If you want get one object from each group (for example most recent) you should try this:
List<PatientHistory> result =
(from r in db.dc_tresult
join t in db.dc_tp_test on r.testid equals t.TestId into x
from t in x.DefaultIfEmpty()
join a in db.dc_tp_attributes on r.attributeid equals a.AttributeId into y
from a in y.DefaultIfEmpty()
where r.prid == prid
group new {r,t,a} by new {r.testid} into g
select new PatientHistory
{
resultid = g.Select(x => x.r.resultid).Last(), // if you expect single value get it with Single()
// .... here add the rest properties
Attribute_Type = g.Select(x => x.a.Attribute_Type).Last()
}).ToList();
I appreciated this question so I thought I would add another potential usage case. I would like feedback on what the cleanest approach is to getting table information through a group operation so that I can project later in the select operation. I ended up combining what the OP did which is to pass objects into his group clause and then used the g.Select approach suggested by YD1m to get table information out later. I have a LEFT JOIN so I'm defending against nulls :
// SQL Query
//DECLARE #idCamp as Integer = 1
//
//select *,
//(select
//count(idActivityMaster)
//FROM tbActivityMasters
//WHERE dftidActivityCategory = A.idActivityCategory) as masterCount
//FROM tbactivitycategories A
//WHERE idcamp = #idCamp
//ORDER BY CategoryName
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new {c, m} by new { m.dftidActivityCategory } into g
select new
{
idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
idCamp = g.Select(x => x.c.idCamp).First(),
CategoryName = g.Select(x => x.c.CategoryName).First(),
CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
masterCount = g.Count(x => x.m != null)
}).OrderBy(o=> o.idActivityCategory);
desiredResult.Dump("desiredResult");
If I just use a basic group approach I get the results but not the extra column information. At least I can't find it once I group.
var simpleGroup = (from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
.OrderBy(o => o.CategoryName)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group m by m == null ? 0 : m.dftidActivityCategory into g
select new
{
// How do I best get the extra desired column information from other tables that I had before grouping
// but still have the benefit of the grouping?
// idActivityCategory = g.Select(x => x.m == null ? 0 : x.m.dftidActivityCategory).First(),
// idCamp = g.Select(x => x.c.idCamp).First(),
// CategoryName = g.Select(x => x.c.CategoryName).First(),
// CategoryDescription = g.Select(x => x.c.CategoryDescription).First(),
// masterCount = g.Count(x => x.m != null)
idActivityCategory = g.Key,
masterCount = g.Count(x => x != null)
});
simpleGroup.Dump("simpleGroup");
Please tear this up. I'm trying to learn and it just seems like I'm missing the big picture here. Thanks.
UPDATE : Cleaned up by moving the work into the group and making the select more straight forward. If I had known this yesterday then this would have been my original answer to the OP question.
int idCamp = 1;
var desiredResult =
(from c in tbActivityCategories
.Where(w => w.idCamp == idCamp)
from m in tbActivityMasters
.Where(m => m.dftidActivityCategory == c.idActivityCategory)
.DefaultIfEmpty() // LEFT OUTER JOIN
where c.idCamp == idCamp
group new { c, m } by new
{ idActivityCategory = m == null ? 0 : m.dftidActivityCategory,
idCamp = c.idCamp,
CateGoryName = c.CategoryName,
CategoryDescription = c.CategoryDescription
} into g
select new
{
idActivityCategory = g.Key.idActivityCategory,
idCamp = g.Key.idCamp,
CategoryName = g.Key.CateGoryName,
CategoryDescription = g.Key.CategoryDescription,
masterCount = g.Count(x => x.m != null)
}).OrderBy(o => o.idActivityCategory);
desiredResult.Dump("desiredResult");

Categories

Resources