Linq GroupBy With Passed Where Predicate - c#

This works fine.g.Key is not null and has appropriate data:
var result = db.JournalEntries.Include(je => je.JournalRecords.Select(jr => jr.Account).Select(j => j.AccountParticulars))
.Where(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To)
.SelectMany(s => s.JournalRecords)
.GroupBy(d => d.AccountParticular.Account.AccountCategory)
.Select(g => new { Name = g.Key.Name });
But this does not work as g.Key is null:
var DateFilter = new Func<JournalEntry, bool>(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To);
var result = db.JournalEntries.Include(je => je.JournalRecords.Select(jr => jr.Account).Select(j => j.AccountParticulars))
.Where(DateFilter)
.SelectMany(s => s.JournalRecords)
.GroupBy(d => d.AccountParticular.Account.AccountCategory)
.Select(g => new { Name = g.Key.Name });
I tried the same thing in a simple console app with static collection and passing in predicate works fine. What could be the problem here?
NOTE: Lazy loading/dynamic proxy is disabled

Try
var DateFilter = new Expression<Func<JournalEntry, bool>>(je => je.Date >= existingLedgerTransaction.From && je.Date <= existingLedgerTransaction.To);
as you need to pass an expression tree to EF

Related

C# Linq Lambda for group by multiple columns select max

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()

MongoDb C# driver 2.4 - LINQ with dynamic GroupBy not supported

var result = await Events.AsQueryable()
.Where(_ => _.Date >= from.Date && _.Date <= to.Date)
.GroupBy(_ => _.SomeProp)
.Select(n => new { n.Key, Count = n.Count() })
.ToListAsync();
works like charm. But lets say that I want to aggregate by custom field - received in aggregateBy string parameter.
So I try:
var propertyToGroupBy = typeof(DiagnosticEvent).GetProperties()
.First(x => x.Name.ToLowerInvariant() == aggregateBy);
And then:
var result = await Events.AsQueryable()
.Where(_ => _.Date >= from.Date && _.Date <= to.Date)
.GroupBy(p => propertyToGroupBy.GetValue(p))
.Select(n => new { n.Key, Count = n.Count() })
.ToListAsync();
Which ends up in:
GetValue of type System.Reflection.PropertyInfo is not supported in
the expression tree System.String Type.GetValue({document})
Any idea how to do it while sticking to LINQ?
I know that i can use Fluent API with:
var group = new BsonDocument { { "_id", $"${aggregateBy}"}, { "Count", new BsonDocument("$sum", 1) } };
But I want to be consistent with LINQ approach.
Full stack trace: https://pastebin.com/RNzXi1kj

How to optimize LINQ-2-SQL expression?

I have a fairly complicated query that would read from a table, then do group on CONTACT_ID, then select only those group with count of 1.
This query is fairly complicated and I have no idea how to optimize it in LINQ.
var linkTable = this.DB.Links
.Where(l=>l.INSTANCE_ID==123456 && l.CONTACT_ID.HasValue && l.ORGANISATION_ID.HasValue)
.Select(l => new { l.DEFAULT_LINKED_ORGANISATION, l.LINK_ID, l.CONTACT_ID });
var defaultOrganizationLinkQuery = linkTable
.Where(l => l.DEFAULT_LINKED_ORGANISATION)
.Select(l => l.LINK_ID);
var singleOrganizationLinkQuery = linkTable
.GroupBy(l => l.CONTACT_ID)
.Select(group => new
{
CONTACT_ID = group.Key,
contact_link_count = group.Count(),
LINK_ID = group.First().LINK_ID
})
.Where(l => l.contact_link_count == 1)
.Select(l => l.LINK_ID);
var merged = singleOrganizationLinkQuery.Union(defaultOrganizationLinkQuery);
I made shorter version, but I do not expect it to be faster. If it works and is not slower I would be satisfied:
var merged = this.DB.Links
.Where(l=>l.INSTANCE_ID==123456 && l.CONTACT_ID.HasValue && l.ORGANISATION_ID.HasValue)
.GroupBy(l => l.CONTACT_ID)
.SelectMany(s => s.Where(x => s.Count() == 1 || x.DEFAULT_LINKED_ORGANISATION)
.Select(link => link.LINK_ID));

OrderByDescending doesn't work in nested linq statement

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

c# getting last element in LINQ statement

var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv").Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderBy(x => x.Date)
.Where(x => x.Date >= LastCreatedDate);
i would like to know the value of the most recent x.Date
from this linq statement how can i get the most recent date?
please note that i do not need the filepath rather i need the DATE
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderBy(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.LastOrDefault();
or
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.FirstOrDefault();
Just reverse the order - also do the filtering before the ordering:
var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv").Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.Where(x => x.Date >= LastCreatedDate)
.OrderByDescending(x => x.Date)
.FirstOrDefault();
Instead I would suggest you use DirectoryInfo's GetFiles() instead which returns FileInfo instances so you don't have to grab the last write time manually:
var di = new DirectoryInfo(#"\\Pontos\completed\");
var file = di.GetFiles("*_*.csv")
.Where(f=> f.LastWriteTimeUtc >= LastCreatedDate)
.OrderByDescending(f => f.LastWriteTimeUtc)
.FirstOrDefault();
if(file!=null)
{
Console.WriteLine("Path: {0}, Last Write Time: {1}", file.FullName,
file.LastWriteTimeUtc);
}
F# has a handy MaxBy() function that I like to use; the C# implementation is trivial. It allows you to avoid the cost of sorting the sequence.
See this answer for more detail: https://stackoverflow.com/a/8759648/385844
usage:
var mostRecent = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.Where(x => x.Date >= LastCreatedDate)
.MaxBy(x => x.Date);
you can use the method .Take(1);
Try this:
var filePaths = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = System.IO.File.GetLastWriteTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= LastCreatedDate)
.FirstOrDefault();
The changes to your statement are the sorting (OrderByDescending instead of OrderBy) to put the newest date "on top" and FirstOrDefault which will select the top, single item from the collection and should result in null if the collection is empty.
To get more file properties you could modify your anonymous object to include more properties, thusly:
var filePath = Directory.GetFiles(#"\\Pontos\completed\", "*_*.csv")
.Select(p => new { Path = p, Date = File.GetLastWriteTime(p), CreatedDate = File.GetCreationTime(p) })
.OrderByDescending(x => x.Date)
.Where(x => x.Date >= DateTime.Now)
.FirstOrDefault();
Console.WriteLine(filePath.Date);
Console.WriteLine(filePath.Path);
Console.WriteLine(filePath.CreatedDate);
Or more succinctly (no need for an anonymous object) you could do this:
var filePath = new DirectoryInfo(#"\\Pontos\completed\").GetFiles("*_*.csv")
.Select(p => p)
.OrderByDescending(p => p.CreationTime)
.Where(x => x.CreationTime >= DateTime.Now)
.FirstOrDefault();
Console.WriteLine(filePath.CreationTime);
Console.WriteLine(filePath.FullName);
As you're using LinqToObjects, if performance is a consideration, you should perhaps consider implementing a MaxBy type method, instead of using OrderBy combined with FirstOrDefault.
I'll find you an implementation. [no need... see #phoog's answer]

Categories

Resources