Optimizing a very large LINQ query - c#

I need to write a/multiple LINQ queries to get some data that I will display for a report.
The tables I need to use are StaffingResources, StaffingForecastEvents (which link a StaffingResource to a Project), and StaffingForecasts (which link to StaffingForecastEvents and contain hours for each week). Each StaffingResource can have 0-many StaffingForecastEvents, and a StaffingForecastEvent can have 0-many StaffingForecasts.
I need to write LINQ query that, for each Resource, will contain all of the projects they have ForecastEvents on, and for each Project all of their Forecasts for a given date range (which is either a 12 weeks or 6 months). Here is what I have so far, and it is running pretty slow.
// Get date ranges
var dates = new List<DateTime>();
var startDate = range == (int)RangeTypes.WEEKLY
? DateTime.Today.AddDays(DayOfWeek.Monday - DateTime.Today.DayOfWeek)
: new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
for (var i = 0; i < (range == (int)RangeTypes.WEEKLY ? 4 : 6); i++)
{
dates.Add(range == (int)RangeTypes.WEEKLY ? startDate.AddDays(i * 7) : startDate.AddMonths(i));
}
var endDate = dates[dates.Count-1];
// Get resources
var resources = from r in context.StaffingResourceDatas
where r.EmployeeId.HasValue
&& (resourceIds.Count == 0 || resourceIds.Contains(r.EmployeeId.Value))
&& (resourceDivisions.Count == 0 || resourceDivisions.Contains(r.ResourceDivisionId))
&& (resourceTitles.Count == 0 || resourceTitles.Contains(r.ResourceTitleId))
&& (resourceLocations.Count == 0 || resourceLocations.Contains(r.ResourceLocationId))
&& (supervisors.Count == 0 || supervisors.Contains(r.ReportsToId))
&& (showAllResources || (!showAllResources && !exclusionList.Contains(r.ResourceTitleId)))
join fe in context.StaffingForecastEvents
.Include(x => x.StaffingForecasts)
.Include(x => x.StaffingUser)
.Include(x => x.StaffingUser1)
.Include(x => x.StaffingForecasts.Select(y => y.StaffingUser))
.Include(x => x.StaffingForecasts.Select(y => y.StaffingUser1))
on r.ResourceId equals fe.ResourceId into g1
from fe in g1.DefaultIfEmpty()
join p in context.StaffingProjectDatas on fe.JobNumber equals p.JobNumber into g2
from p in g2.DefaultIfEmpty()
group new { ForecastEvent = fe, Project = p } by r into g3
select new
{
ResourceId = g3.Key.ResourceId,
Name = g3.Key.ResourceName,
Title = g3.Key.ResourceTitle,
Division = g3.Key.ResourceDivision,
Location = g3.Key.ResourceLocation,
AvailableDate = g3.Key.AvailableDate,
SupervisorEmail = g3.Key.ManagerEmail,
Projects = g3.Where(p => p.ForecastEvent != null).Select(p => new
{
JobNumber = p.ForecastEvent.JobNumber,
Description = p.Project.ProjectDescription,
Name = p.Project.ProjectName,
Division = p.Project.ProjectDivision,
ProjectManager = p.Project.PMName,
Notes = p.ForecastEvent.Notes,
LogDate = p.ForecastEvent.LogDate,
LogUser = p.ForecastEvent.StaffingUser.Name,
AckDate = p.ForecastEvent.AcknowledgeDate,
AckUser = p.ForecastEvent.StaffingUser1 != null ? p.ForecastEvent.StaffingUser1.Name : null,
Usages = dates.Select(d => new
{
Date = d,
Hours = (range == (int)RangeTypes.WEEKLY)
? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any() ? p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Sum(f => f.Hours) : 0)
: (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any() ? p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Sum(f => f.Hours) : 0),
LogDate = (range == (int)RangeTypes.WEEKLY)
// Get acknowledge or log date for week
? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any()
? ((p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate) != null)
? (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate)
: (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).LogDate)
: null)
// Get acknowledge or log date for most recent forecast for month
: (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any()
? ((p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate) != null)
? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate)
: (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Max(f => f.LogDate))
: null),
LogUser = (range == (int)RangeTypes.WEEKLY)
// Get acknowledge or log user for week
? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date == d).Any()
? ((p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).AcknowledgeDate) != null)
? (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).StaffingUser1.Name)
: (p.ForecastEvent.StaffingForecasts.FirstOrDefault(f => f.Date == d).StaffingUser.Name)
: null)
// Get acknowledge or log user for most recent forecast for month
: (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).Any()
? ((p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().AcknowledgeDate) != null)
? (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().StaffingUser1.Name)
: (p.ForecastEvent.StaffingForecasts.Where(f => f.Date.Year == d.Year && f.Date.Month == d.Month).OrderByDescending(f => f.LogDate).FirstOrDefault().StaffingUser.Name)
: null),
})
})
};
I will also need to get totals for each resource for each date range.
I feel like the bottleneck is probably with all the "Where"s when looping through the dates, but I don't know what else to do. Any ideas?

As a starting point you can split it up into several expressions using IQueryable<T>. It would also help you make your where clauses simpler. That would look something like:
var queryable = context.StaffingResourceDatas.Where(r => r.EmployeeId.HasValue);
if(resourceIds.Any())
{
queryable = queryable.Where(r => resourceIds.Contains(r.EmployeeId.Value))
}
Notice that this will generate SQL related to filtering by resourceIds only if resourceIds is not empty, thus potentially saving the overhead of checking it in the generated query itself.
You can write the rest of the filters similarly to this one. Also notice that query won't get executed until you call ToList() on it. So you can keep adding as many clauses as you like, until you're done constructing it.
But at the end of the day you might want to consider writing this one in raw SQL because it's just, you know, gigantic.

Related

how i get list of one week data with count and date

I have try to get list of data for last on week date with count .the problem is if the count zero the date not in that list but i want all seven days with count
lastWeekRegisterStatistic1 = dbContext.MtMUsers.Join(dbContext.MtLogRegistrationAudits,
U => U.EmployeeId,
Ru => Ru.UserId,
(U, Ru) => new { U = U, Ru = Ru })
.Where(x => x.Ru.Status != null &&
x.Ru.Status.ToLower() == "success" &&
x.Ru.LoggedDate.Value.Date >= dt.Date &&
x.Ru.LoggedDate.Value.Date <= dt.AddDays(6).Date &&
x.U.IsMobileUser != 0 &&
(request.clientIds == null ||
request.clientIds.Count == 0 ||
request.clientIds.Contains(x.U.ClientId)) &&
(request.assetIds == null ||
request.assetIds.Count == 0 ||
request.assetIds.Contains(x.U.AssetId)) &&
((request.startDate == null && request.endDate == null) ||
(request.startDate <= x.U.CreatedDate && request.endDate >= x.U.CreatedDate)))
.GroupBy(g => new { g.Ru.LoggedDate.Value.Date }).OrderByDescending(o => o.Count())
.Select(s => new lastWeekStatisticsObject
{
date = s.Key.Date,
userCount = s.Count()
}).OrderBy(o => o.date).ToList();
this is my code
result is
"lastWeekStatistics": [
{
"date": "2022-11-10T00:00:00",
"userCount": 166
},
{
"date": "2022-11-11T00:00:00",
"userCount": 132
},
{
"date": "2022-11-12T00:00:00",
"userCount": 17
}
I only get three date data because balance date don't have data
I want all the seven days with date and count
Can't you just append the non-existing dates after the query manifested as List?
var emptyDates = Enumerable.Range(0, 6)
.Select(x => dt.Date.AddDays(x))
.Where(x =>
!lastWeekRegisterStatistic1
.Select(y => y.date)
.Contains(x))
.Select(x => new lastWeekStatisticsObject()
{
date = x,
userCount = 0
});
Then you can append the result using normal List operation:
lastWeekRegisterStatistic1.Add(emptyDates);
Don't forget to sort the List though!

How to write a linq query to exclude some of the records?

This is my LINQ
IList<string> ExceptList = new List<string>() { "045C388E96", "C9B735E166", "02860EB192", "2401016471" };
var listusers = context.USER_INFO.Where(x => x.ACTIVATED
&& x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE) > 0
&& (x.LAST_LOGIN < time)
&& !ExceptList.Contains(x.COMP.CODE)
&& !x.IS_LOCK
|| !x.COMP.IS_LOCK)
.Select(x => new EmailOutOfDateLoginModel
{
COMPCode = x.COMP.CODE,
First_Name = x.FIRST_NAME,
Last_Name = x.LAST_NAME,
Total_EQ = x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE),
User_Email = x.USER_EMAIL
}).ToList();
I am not sure why my ExceptList is not working. I want to exclude any record that contaisn any of the CODE in the ExceptList
Put parentheses around the expressions containing the && logic. The || at the end is only matched with the !x.IS_LOCK || !x.COMP.IS_LOCK otherwise.
According your linq all records where (!x.COMP.IS_LOCK==true) will be included in the query. Try this "where" part:
.Where(x => x.ACTIVATED
&& x.COMP.EQUIPMENT.Count(y => y.STATUS == (int)STATUSEQ.ACTIVE) > 0
&& (x.LAST_LOGIN < time)
&& !ExceptList.Contains(x.COMP.CODE)
&& !(x.IS_LOCK && x.COMP.IS_LOCK))

Include where clause on linq query when param is not null Npgsql

I have following method that registers a contact in database, but before register I check the contact exists or not:
bool RegisterContact(Contact contactInfo) {
bool entityExists =
_dbContext.Contacts.FirstOrDefault(
p => (p.FilesID.Equals(contactInfo.FilesID))
&& (p.EmailAddress ==
(string.IsNullOrEmpty(
contactInfo.EmailAddress)
? p.EmailAddress
: contactInfo.EmailAddress))
&&
(p.DisplayName ==
(string.IsNullOrEmpty(
contactInfo.DisplayName)
? p.DisplayName
: contactInfo.DisplayName)));
}
this query includes the fields that contain value (not null) in search condition (FilesID, EmailAddress, DisplayName)
this technique works fine in MSSQL, today i changed the database manager to PostgreSQL and use Npgsql.
All things work except above linq query, which raises an exception with message of : "could not determine data type of parameter $2"
I was forced to solve it in this way:
bool RegisterContact(Contact contactInfo)
{
Contact entityExists = null;
if (string.IsNullOrEmpty(contactInfo.EmailAddress) &&
(string.IsNullOrEmpty(contactInfo.DisplayName)))
entityExists =
_dbContext.Contacts.FirstOrDefault(
p => p.FilesID.Equals(contactInfo.FilesID));
if (!string.IsNullOrEmpty(contactInfo.EmailAddress) && string.IsNullOrEmpty(contactInfo.DisplayName))
entityExists =
_dbContext.Contacts.FirstOrDefault(
p =>
p.FilesID.Equals(contactInfo.FilesID) &&
p.EmailAddress == contactInfo.EmailAddress);
if (string.IsNullOrEmpty(contactInfo.EmailAddress) && !string.IsNullOrEmpty(contactInfo.DisplayName))
entityExists =
_dbContext.Contacts.FirstOrDefault(
p =>
p.FilesID.Equals(contactInfo.FilesID) &&
p.DisplayName == contactInfo.DisplayName);
if (!string.IsNullOrEmpty(contactInfo.EmailAddress) &&
!string.IsNullOrEmpty(contactInfo.DisplayName))
entityExists =
_dbContext.Contacts.FirstOrDefault(
p =>
p.FilesID.Equals(contactInfo.FilesID) &&
p.EmailAddress == contactInfo.EmailAddress &&
p.DisplayName == contactInfo.DisplayName);
}
Is this Npgsql bug or by design? any known solutions/workarounds for the problem?
I currently have the same cases. I think the problem is the lack of recognition, by NpgSQL, of string.IsNullOrEmpty.
I replaced the test with a check on empty string, always recognizing as not NULL the input parameter.
-- bad
var data = from art in _ctx.Set<Soleo.Model.DLAR>()
from iva in _ctx.Set<Soleo.Model.DLAI>().Where(k => k.DITTA == art.DITTA && k.COD == art.CIVA).DefaultIfEmpty()
from fam in _ctx.Set<Soleo.Model.DLFA>().Where(k => k.DITTA == art.DITTA && k.COD == art.FAM).DefaultIfEmpty()
from mar in _ctx.Set<Soleo.Model.DLMA>().Where(k => k.DITTA == art.DITTA && k.COD == art.MAR).DefaultIfEmpty()
from udm in _ctx.Set<Soleo.Model.DLUM>().Where(k => k.DITTA == art.DITTA && k.COD == art.UM).DefaultIfEmpty()
where art.DITTA == DLAUTH.Config.Current.DITTA && art.COD.Contains(sel_cod) && art.DES.Contains(sel_des)
&& (string.IsNullOrEmpty(sel_fam) || string.Compare(art.FAM, sel_fam, true) == 0)
&& (string.IsNullOrEmpty(sel_mar) || string.Compare(art.MAR, sel_mar, true) == 0)
&& (art.DIS >= sel_dis_da && art.DIS <= sel_dis_a)
select new
{
COD = art.COD,
DES = art.DES,
DES_UDM = udm.DES,
DES_MAR = mar.DES,
DES_FAM = fam.DES,
DES_CIVA = iva.DES,
MAG1 = art.MAG1,
MAG2 = art.MAG2,
DES_DIS = art.DIS == 1 ? "Si" : "No"
};
-- good:
var data = from art in _ctx.Set<Soleo.Model.DLAR>()
from iva in _ctx.Set<Soleo.Model.DLAI>().Where(k => k.DITTA == art.DITTA && k.COD == art.CIVA).DefaultIfEmpty()
from fam in _ctx.Set<Soleo.Model.DLFA>().Where(k => k.DITTA == art.DITTA && k.COD == art.FAM).DefaultIfEmpty()
from mar in _ctx.Set<Soleo.Model.DLMA>().Where(k => k.DITTA == art.DITTA && k.COD == art.MAR).DefaultIfEmpty()
from udm in _ctx.Set<Soleo.Model.DLUM>().Where(k => k.DITTA == art.DITTA && k.COD == art.UM).DefaultIfEmpty()
where art.DITTA == DLAUTH.Config.Current.DITTA && art.COD.Contains(sel_cod) && art.DES.Contains(sel_des)
&& (string.Compare(sel_fam, "", true) == 0 || string.Compare(art.FAM, sel_fam, true) == 0)
&& (string.Compare(sel_mar, "", true) == 0 || string.Compare(art.MAR, sel_mar, true) == 0)
&& (art.DIS >= sel_dis_da && art.DIS <= sel_dis_a)
select new
{
COD = art.COD,
DES = art.DES,
DES_UDM = udm.DES,
DES_MAR = mar.DES,
DES_FAM = fam.DES,
DES_CIVA = iva.DES,
MAG1 = art.MAG1,
MAG2 = art.MAG2,
DES_DIS = art.DIS == 1 ? "Si" : "No"
};
But I do not think this is the solution. I will report the case to NpgSQL.

C# use left outer join in LINQ

This is my method:
public IEnumerable<Web_Vendor> GetSaleBrands()
{
using (var _db = new LuxedecorContext())
{
IQueryable<Web_Vendor> web_vendors = _db.Web_Vendor.
Where(x =>
(_db.Web_Promotion.Where(z => z.VendorID != string.Empty
&& z.Static == true && z.Percent != 0 &&
(x.WebPromotion.Expires >= DateTime.Now || x.WebPromotion.Expires == null))
.Select(r => r.VendorID).Contains(x.VendorID))
||
(_db.NavItems.Where(y => x.WebPromotion.VendorID == y.SC1
&& !(y.Promotion == "" || y.Promotion == null)
&& (y.PromotionStart <= DateTime.Now) && (y.PromotionEnd >= DateTime.Now || y.PromotionEnd == null))
.Select(g => g.SC1).Contains(x.WebPromotion.VendorID)))
.Where(x => x.Active == true).OrderBy(x => x.NameExtra)
.ThenBy(x => x.Sequence);
var a = web_vendors.ToList(); // 16 values
var b = web_vendors.Include(x => x.WebPromotion).ToList(); // 13 values
}
throw new NotImplementedException();
}
I need to Include nullable values to my collection of Web_Vendors. I know, it's famous question, but I need to return collection of Web_Vendors instead of collection of dynamic types. Is it possible?

how to show result using multiple Order by descending using linq query

I am having 3 criteria. I want to order these 3 types.
Who Paid with Master User
Who Update their Post Latest Date
Who Paid with Sub Master User
which one is having count it will come to top 15 Jobs.
My code here:
var orderMaster= _vasRepository.GetOrderDetails()
.Where(od => od.OrderMaster.OrganizationId != null &&
od.OrderId == od.OrderMaster.OrderId &&
od.OrderMaster.PaymentStatus == true &&
od.ValidityTill.Value >= currentdate)
.OrderByDescending(od => od.ValidityTill)
.Select(ord => ord.OrderMaster.Id.Value);
var updatedVacancyList = _repository.GetJobs()
.Where(c => c.UpdatedDate != null &&
updateFresh <= c.UpdatedDate)
.Select(c => c.Id);
var orderLatestUser = _vasRepository.GetOrderDetails()
.Where(od => od.OrderMaster.UserId != null &&
od.OrderMaster.PaymentStatus == true &&
freshUser <= od.ActivationDate &&
od.ValidityTill.Value >= currentdate)
.Select(c => c.OrderMaster.User.Id);
Then I check the count of those then assign to
List<int> lstMasterId = orderOrganization.ToList();
List<int>lstUpdatedJobsListId = updatedVacancyList.ToList();
List<int>lstUserListId= orderLatestUser.ToList();
Here i order the lists using query
Func<IQueryable<Job>, IOrderedQueryable<Job>> orderingFunc = query =>
{
if (orderMaster.Count() > 0)
return query.OrderByDescending(rslt =>
lstOrganizationId.Contains(rslt.OrganizationId))
.ThenByDescending(rslt=>lstUserListId.Contains(rslt.User.Id))
.ThenByDescending(rslt => lstUpdatedJobsListId.Contains(rslt.Id))
.ThenByDescending(rslt => rslt.CreatedDate);
else
return query.OrderByDescending(rslt => rslt.CreatedDate);
};
jobs = orderingFunc(jobs);
}
I want to show the lstUserListId at top of the result.. How to do this?

Categories

Resources