How to optimize LINQ-2-SQL expression? - c#

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

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

How do i do the "IN" and "DISTINCT of SQL to LINQ

SELECT DISTINCT MarketTemplateId AS MarketTemplateID
FROM Market
WHERE Market.FixtureId = ? And
Market.MarketTemplateId In
('MW3W', '1HTG', 'FTCS', 'HTFT', 'MTG2W', 'MW3W1H','FTCSALT')
how would i produce this in LINQ because i tried the below code and it doesn't work
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == InternalFixtureID_F.ToString())
.FirstOrDefault(p => MarketTemplateIds.Contains(p.MarketTemplateId));
List<string> MarketTemplateIds = new List<string>{ "MW3W", "1HTG", "FTCS", "HTFT", "MTG2W", "MW3W1H","FTCSALT" };
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == InternalFixtureID_F.ToString()
and MarketTemplateIds.Contains(p.MarketTemplateId ))
.Select( u => u.MaketTemplateId).Distinct();
How about
var id = InternalFixtureID_F.ToString();
var result6 = entityTrading.Markets
.Where(p => p.FixtureId == id && MarketTemplateIds.Contains(p.MarketTemplateId))
.Select(m => m.MarketTemplateId)
.Distinct();

Union of ordered sets in entity framework and then skip

I have a table of lessons, and I want to perform a text search over several fields of it. However the search should be ordered: for example lesson have a Keywords field and Description field. The search should give a priority over values found by Keywords. Everything should be also ordered by date but only after the priority is considered.
I'm also using ToPagedList() in the end from https://github.com/troygoode/PagedList (I think it just uses Skip() and Top() to manage pages)
This is what I have so far:
string[] word = /*Search words*/
var data = db.LessonsLearneds.Where(dbRecord => words.Any(word =>
dbRecord.SearchKeywords.StartsWith(word + ",") ||
dbRecord.SearchKeywords.Contains("," + word + ",") ||
dbRecord.SearchKeywords.EndsWith("," + word)))
.Select(x => new { Record = x, Order = 1 });
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Title.Contains(word)))
.Select(x => new { Record = x, Order = 2 }));
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Description.Contains(word)))
.Select(x => new { Record = x, Order = 3}));
data = data.Union(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Lesson.Contains(word)))
.Select(x => new { Record = x, Order = 4 }));
return data
.Distinct()
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
Overall this code does almost what I want, except of Distinct(). Each union here can retrieve the same record, so I may receive it several times, and Distinct() does not forces the uniqueness because of virtual Order field. I cannot put Distinct after Select(x => x.Record) because of ToPagedList(..) which requires the set to be ordered (results in: The method 'Skip' is only supported for sorted input in LINQ to Entities. exception)
Any ideas?
I have one so far: to add Order field after I Distinct, but this means that I will have to write those Contains checks twice which I think is very ugly solution.
First, since you are projecting unique records due to the different Order value, replace the Union operator with Concat (which is the LINQ equivalent of the SQL UNION ALL).
string[] word = /*Search words*/
var data = db.LessonsLearneds.Where(dbRecord => words.Any(word =>
dbRecord.SearchKeywords.StartsWith(word + ",") ||
dbRecord.SearchKeywords.Contains("," + word + ",") ||
dbRecord.SearchKeywords.EndsWith("," + word)))
.Select(x => new { Record = x, Order = 1 });
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Title.Contains(word)))
.Select(x => new { Record = x, Order = 2 }));
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Description.Contains(word)))
.Select(x => new { Record = x, Order = 3}));
data = data.Concat(
db.LessonsLearneds
.Where(dbRecord => words.Any(word => dbRecord.Lesson.Contains(word)))
.Select(x => new { Record = x, Order = 4 }));
Then replace the Distinct with GroupBy using x.Record as a key and taking min Order for each grouping, and do the rest as in your current query:
return data
.GroupBy(x => x.Record)
.Select(g => new { Record = g.Key, Order = g.Min(x => x.Order) })
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
You can replace Distinct with GroupBy and Select, like this:
return data
.GroupBy(x => x.Record)
.Select(g => g.OrderBy(x => x.Order).ThenByDescending(x => x.Record.Date).First())
.OrderBy(x => x.Order)
.ThenByDescending(x => x.Record.Date)
.Select(x => x.Record)
.ToPagedList(pageNumber, pageSize);
The unfortunate side effect of this approach is that you need to repeat OrderBy inside the first Select, but it should produce the results that you are looking for.

Grouping earliest entry for each day using Linq

Trying to get my head around Linq, and at the same time keep track of the time I log on in the morning, which should be the time I get into the office thereabouts.
My code so far is:
EventLog SecurityLog = new EventLog("Security");
var AccountLoggedOnEntries = SecurityLog.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();
DgvLogSummary.DataSource = AccountLoggedOnEntries;
DgvLogSummary.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
I want to filter the results so that I only have one entry for each day, which is the earliest time.
In SQL I would normally take the Message of the earliest entry and then group by all fields.
How do I perform a similar query in Linq?
In LINQ you would group by, sort each group, and pick the first item:
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(g => g.OrderBy(x => x.TimeGenerated).First())
.Select(x => new {
DateGenerated = x.TimeGenerated.ToShortDateString()
, TimeGenerated = x.TimeGenerated.ToShortTimeString()
, x.Message
})
.ToList();
You could GroupBy the date and then select the minimum time
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(x => new {
DateGenerated = x.Key
, TimeGenerated = x.Min(y => y.TimeGenerated).ToShortTimeString()
})
.ToList();
Getting the appropriate Message is a little more tricky. One easy option is to use x.First().Message in the above Select projection.
Try this :
var AccountLoggedOnEntries = log.Entries.Cast<EventLogEntry>()
.Where(x => x.InstanceId == 4624)
.GroupBy(x => x.TimeGenerated.Date)
.Select(days => days.OrderBy(time => time.TimeGenerated).FirstOrDefault())
.Select(x => new
{
DateGenerated = x.TimeGenerated.ToShortDateString()
,
TimeGenerated = x.TimeGenerated.ToShortTimeString()
,
x.Message
})
.ToList();

Linq GroupBy With Passed Where Predicate

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

Categories

Resources