I have a list of FlightTicketRequestDocument used for both Patients and their Escorts requesting a plane ticket. Theses are grouped with a GUID named GroupId.
I'm trying to group them inside this object:
public class FlightTicketRequestDocumentGroup
{
public Guid GroupId { get; set; }
public FlightTicketRequestDocument PrincipalDocument { get; set; }
public List<FlightTicketRequestDocument> GroupDocuments { get; set; }
}
PrincipalDocument holds the Patient if there's one. (If there isn't we just want the first Escort)
GroupDocuments holds the Escorts. (Except the first one that is placed in PrincipalDocument)
I'm attempting to use different expressions to set my values inside a Select New statement depending if there's a Patient or not but I'm getting the error:
The nested query is not supported. Operation1='Case' Operation2='VarRef'
Here's the code (Note: StayEscortId is null when the person is a Patient.):
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.Any(f => f.StayEscortId == null)
? g.FirstOrDefault(f => f.StayEscortId == null)
: g.FirstOrDefault(),
GroupDocuments = g.Any(f => f.StayEscortId == null)
? g.Where(c => c.StayEscortId != null).ToList()
: g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
What can I use instead of the conditional operator ?: ?
The solution that I ended up using:
As specified by #mjwills, since I simply want the values where StayEscordId is null first, all I need to do is to sort as a boolean (false values end up at the top).
For the rest of the group, I do the same operation while Skipping 1 (the first one I chose).
Modified code:
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.OrderBy(f => f.StayEscortId != null).ThenBy(f => f.FullName).Skip(1).ToList()
})
.ToListAsync(cancellationToken);
Here's what the query would look like if you were to use if/else instead of the ternary conditions. You would just use a code block inside of the .Select() to return the results:
var flightTicketRequestDocuments = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.ToListAsync(cancellationToken);
var indexGroups = flightTicketRequestDocuments
.Select(g =>
{
FlightTicketRequestDocument principalDocument;
List<FlightTicketRequestDocument> groupDocuments;
if (g.Any(f => f.StayEscortId == null))
{
principalDocument = g.FirstOrDefault(f => f.StayEscortId == null);
groupDocuments = g.Where(c => c.StayEscortId != null).ToList();
}
else
{
principalDocument = g.FirstOrDefault();
groupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(1).ToList();
}
return new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = principalDocument,
GroupDocuments = groupDocuments
};
})
.ToList();
Note that this approach requires enumerating the IQueryable before performing the .Select() so that it's applied against an IEnumerable.
Needs a check or two (or a tweak or two) but if i remember correctly expression trees accept null coalescence operator you may try to swap in the place of the ternary.
Starting (and BASING) from your example, maybe something like the following?
var indexGroups = await _db.FlightTicketRequests
.OrderBy(f => f.FirstName)
.ProjectTo<FlightTicketRequestDocument>()
.GroupBy(f => f.GroupId)
.Select(g => new FlightTicketRequestDocumentGroup
{
GroupId = g.Key,
PrincipalDocument = g.FirstOrDefault(f => f.StayEscortId == null) ?? g.OrderBy(f => f.FullName).FirstOrDefault(),
GroupDocuments = g.Where(c => c.StayEscortId != null).OrderBy(f => f.FullName).Skip(Math.Max(x.Count(y => y.StayEscortId == null), 1)).ToList()
})
.ToListAsync(cancellationToken);
Related
I have a following query
return _context.Table1
.Where(x => x.ContactId == contactKey.Id)
.Include(x => x.Table2)
.Include(x => x.Table3.Table4)
.Select(a =>
new MyReadModel
{
PriorityAssignment = true,
LastContactedDate = (a.Table3.Table4 != null && a.Table3.Table4.FirstOrDefault(h =>
h.Id == a.Table2.FkId
) != null ?
a.Table3.Table4.FirstOrDefault(h => && h.Id == a.Table2.FkId
).LastContactedDatetime : default
)
}
)
.ToListAsync();
What i wants is to simplify LastContactedDate assignment with in select. I think we can assign
a.Table3.Table4.FirstOrDefault(h =>
h.Id == a.Table2.FkId
)
to some variable but can't able to do it
can someone identify what is needed
With EF Core you don't have to check for null, LINQ Translator do not execute your code, but uses it for translation to the SQL. Also Includes is not needed if you do not retrieve whole objects.
return await _context.Table1
.Where(x => x.ContactId == contactKey.Id)
.Select(a => new MyReadModel
{
PriorityAssignment = true,
LastContactedDate = (DateTime?)a.Table3.Table4.Where(h => h.Id == a.Table2.FkId)
.OrderByDescending(h => LastContactedDatetime)
.Select(h => LastContactedDatetime)
.FirstOrDefault()
}).ToListAsync();
you can use it like this example
List<string> someList= new List<string>();
someList= someList.Select(x =>
{
var newVariable= "newVariable";
return newVariable;
}).ToList();
in your case
return _context.Table1
.Where(x => x.ContactId == contactKey.Id)
.Include(x => x.Table2)
.Include(x => x.Table3.Table4)
.Select(a => {
Table4 newVariable = null;
if(a.Table3.Table4 != null)
newVariable = a.Table3.Table4.FirstOrDefault(h => h.Id == a.Table2.FkId;
var result = new MyReadModel
{
PriorityAssignment = true,
LastContactedDate = (newVariable != null ? newVariable.LastContactedDatetime : default
)
};
}
) .ToListAsync();
I want to translate this into lambda syntax and can't seem to get it to work:
Grouping by two columns, select max on a different column, return list of complete complex object.
I am writing more text here to get past the validation on this form. How much text is needed until I am allowed to post this?
_clientpolicies = (from policy in
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
group policy by
new
{
PolicyReference = GetPolicyReference(policy),
PolicyType = policy.ProductInformation.PolicyTypeCode
}
into g
let maxPolicyInception = g.Max(p => p.InceptionDate)
from policyGroup in g
where policyGroup.InceptionDate == maxPolicyInception
select policyGroup).ToList();
I dont think there's a way of doing it in one line. So there's my try :
policyGroups=
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm
.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x => GetPolicyReference(x))
.ThenBy(x => x.ProductInformation.PolicyTypeCode)
.ToList();
var maxPolicyInception = policyGroups.Max(p => p.InceptionDate);
_clientpolicies = policyGroups
.Where(g => g.InceptionDate == maxPolicyInception)
.ToList();
_clientpolicies =
_reply.CommercialInsuredGroupWithPolicyTerm.InsuredWithPolicyTerm.SelectMany(x => x.PolicyTerm)
.Where(x => !(string.IsNullOrWhiteSpace(x.PolicyNumber) && string.IsNullOrWhiteSpace(x.ControlNumber)))
.Where(x => x.Insured.DNBAccountNumber == _client.LookupID)
.GroupBy(x =>
new
{
PolicyReference = GetPolicyReference(x),
PolicyType = x.ProductInformation.PolicyTypeCode
},
(key, g) => g.OrderByDescending(gx => gx.InceptionDate).First()
I've written this code which works:
var uniqueCustomerIdList = services
.GroupBy(x => x.CustomerId)
.Select(cl => new Customer
{
CustomerId = cl.First().CustomerId,
CustomerName = cl.First().CompanyName,
PdfServices = services.Where(x => x.CustomerId == cl.First().CustomerId).ToList(),
PdfServiceLines = services.Where(x => x.CustomerId == cl.First().CustomerId).ToList()
.GroupBy(l => l.ServiceDescription)
.Select(cy => new PdfServiceLine
{
ServiceName = cy.First().ServiceDescription,
Quantity = cy.Count(),
UnitPrice = cy.First().PlanCharge,
ServiceCharges = services.Where(x => x.CustomerId == cl.First().CustomerId && x.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.TotalBill),
UsageCharges = usage.Where(x => x.CustomerId == cl.First().CustomerId && x.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.Charge),
Total = cy.Sum(c => c.PlanCharge),
}).ToList(),
PdfUsages = usage.Where(x => x.CustomerId == cl.First().CustomerId).ToList()
})
.ToList();
I wanted to know if it's possible to reference values from the outer Select statement in the inner statement? As it looks rather clunky at the moment.
For instance in the outer Customer select I use PdfServices - can I use that in the inner select where I have ServiceCharges?
ServiceCharges = PdfServices.Where(s => s.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.TotalBill)
instead of
ServiceCharges = services.Where(x => x.CustomerId == cl.First().CustomerId && x.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.TotalBill),
Thanks,
Lee.
The results that you want to re-use are members of an anonymous object, not variables. As such, you cannot expect them to be available, like a variable would be, to the inner lambda. If you re-wrote your statement, you could store them in intermediate variables:
var uniqueCustomerIdList = services
.GroupBy(x => x.CustomerId)
.Select(cl =>
{
var pdfServices = services.Where(x => x.CustomerId == cl.First().CustomerId).ToList();
return new Customer
{
CustomerId = cl.First().CustomerId,
CustomerName = cl.First().CompanyName,
PdfServices = pdfServices,
PdfServiceLines = pdfServices
.GroupBy(l => l.ServiceDescription)
.Select(cy => new PdfServiceLine
{
ServiceName = cy.First().ServiceDescription,
Quantity = cy.Count(),
UnitPrice = cy.First().PlanCharge,
ServiceCharges = services.Where(x => x.CustomerId == cl.First().CustomerId && x.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.TotalBill),
UsageCharges = usage.Where(x => x.CustomerId == cl.First().CustomerId && x.ServiceDescription == cy.First().ServiceDescription).Sum(y => y.Charge),
Total = cy.Sum(c => c.PlanCharge),
}).ToList(),
PdfUsages = usage.Where(x => x.CustomerId == cl.First().CustomerId).ToList()
};
})
.ToList();
Note that the lamda body now uses curly braces and a return statement. This is not convertible to an expression tree, and some ORM frameworks, like Entity Framework, will not be able to translate the lamda into SQL.
I want add a new column to find which is lasted record in group.
Can I write subquery in Select() method?
I have try this
var test = DailyPeriods.Where(x => x.BookingDate == "2016/12/30")
.Select(x =>
new
{
PERIOD_GROUP_ID = x.PeriodGroupID,
PERIOD_NAME = x.PeriodName,
New_Column = DailyPeriods
.Where(z => z.BookingDate == "2016/12/30")
.Select(a =>
new
{
PeriodGroupID = a.PeriodGroupID,
period_name = a.PeriodName
}
)
.GroupBy(b => b.period_name)
.Select(g => g.Last().PeriodGroupID)
.Contains(x.PeriodName)
})
But will occur this error
"column not in scope: A:2211708.C(BOOKING_DATE)"
Try this..
var lastRecords = periodList.GroupBy(l => l.PeriodName)
.Select(x => new { PeriodName = x.Key,
PeriodGroupId = x.OrderBy(l => l.PeriodGroupId).Last().PeriodGroupId});
var result = from period in periodList
from lastRec in lastRecords.Where(r => r.PeriodGroupId == period.PeriodGroupId
&& r.PeriodName == period.PeriodName)
.DefaultIfEmpty()
select new { period.PeriodGroupId,period.PeriodName, New_Column=lastRec==null?false:true };
In Linqpad, i can see the correct list. But in code, after putting in the list collection, order by doesn't work for BeginDate. If i use BeginDate with Max, it works. I don't understand where i am wrong?
var templist = contentRepository
.Get(q => (q.Status == (int)StatusEnum.Active) &&
(q.CategoryId == category.GetHashCode() || q.Category.ParentId == category.GetHashCode())
&& q.MinorVersion == 0
&& q.MajorVersion > 0)
.GroupBy(q => q.VersionId)
.OrderByDescending(q => q.Key)
.Select(q => new
{
VersionId = q.Key,
Id = q.Max(x => x.Id),
MajorVersion = q.Max(x => x.MajorVersion),
UpdatedAt = q.Max(x => x.UpdatedAt),
//BeginDate = q.Max(x=>x.BeginDate),
BeginDate = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.BeginDate)).First(),
Title = (q.OrderByDescending(x => x.Id).Take(1).Select(x => x.Title)).First(),
ShowOnHomePage = (q.OrderByDescending(x => x.Id).Take(1).Select(x=>x.ShowOnHomePage)).First()
})
.OrderByDescending(x => x.BeginDate)
.Take(maxItemCount)
.ToList();
List<ContentEntity> contents = new List<ContentEntity>();
templist.ForEach(q => contents.Add(
contentRepository
.Get(x => x.VersionId == q.VersionId && x.MajorVersion == q.MajorVersion && x.MinorVersion == 0)
.FirstOrDefault()
));
return contents.Where(q => q.ShowOnHomePage == true)
.OrderByDescending(q => q.MajorVersion)
.OrderByDescending(q => q.BeginDate)
.Take(maxItemCount)
.ToList();
You are ordering by Id, not by BeginDate. Equivalent code for
q.Max(x => x.BeginDate)
Will be
q.OrderByDescending(x => x.BeginDate).Take(1).Select(x => x.BeginDate).First()
Or simplified
q.OrderByDescending(x => x.BeginDate).First().BeginDate