I have a query that processes about 500 records pulled from various tables, grouped and then summaried (if that's a word) into a working report. Everything works fine but it takes about 30 seconds to run this one report and i'm getting complaints from my users.
The procedure in question is this one:
public static List<LabourEfficiencies> GetLabourEfficienciesByTimeSheet(DateTime dateFrom, DateTime dateTo)
{
CS3Entities ctx = new CS3Entities();
//get all relevant timesheetline items
var tsItems = from ti in ctx.TimeSheetItems
where ti.TimeSheetHeader.Date >= dateFrom && ti.TimeSheetHeader.Date <= dateTo && ti.TimeSheetHeader.TimeSheetCategory != "NON-PROD"
select new TimesheetLine
{
TimesheetNo = ti.TimeSheetNo,
HoursProduced = ti.HoursProduced,
HoursProducedNet = ti.HoursProducedNet,
ItemID = ti.ItemID,
ProcessID = ti.ProcessID,
ProcessDuration = ti.ProcessDuration,
DowntimeHours = 0M
};
//get all relevant downtimeline items
var tsDownT = from dt in ctx.DowntimeItems
where dt.TimeSheetHeader.Date >= dateFrom && dt.TimeSheetHeader.Date <= dateTo && dt.TimeSheetHeader.TimeSheetCategory != "NON-PROD"
select new TimesheetLine
{
TimesheetNo = dt.TimeSheetNo,
HoursProduced = 0M,
HoursProducedNet = 0M,
ItemID = "",
ProcessID = "",
ProcessDuration = 0M,
DowntimeHours = dt.DowntimeHours
};
//combine them into single table
var tsCombi = tsItems.Concat(tsDownT);
var flatQuery = (from c in tsCombi
join th in ctx.TimeSheetHeaders on c.TimesheetNo equals th.TimeSheetNo
select new
{
th.TimeSheetNo,
th.EmployeeNo,
th.TimeSheetCategory,
th.Date,
c.HoursProduced,
c.ProcessDuration,
th.HoursWorked,
c.HoursProducedNet,
c.DowntimeHours,
c.ItemID
});
//add employee details & group by timesheet no (1 line per timesheet no)
//NB. FnTlHrs checks whether there are any indirect hrs & deducts them if there are
var query = flatQuery.GroupBy(f => f.TimeSheetNo).Select(g => new LabourEfficiencies
{
Eno = g.FirstOrDefault().EmployeeNo,
Dept =g.FirstOrDefault().TimeSheetCategory,
Date = g.FirstOrDefault().Date,
FnGrHrs =g.Where(w =>w.TimeSheetCategory == "FN" &&!w.ItemID.StartsWith("090")).Sum(h => h.HoursProduced),
FnTlHrs =g.Where(w =>w.ItemID.StartsWith("090")).Sum(h => h.ProcessDuration) >0? (g.FirstOrDefault(w =>w.TimeSheetCategory =="FN").HoursWorked) -(g.Where(w =>w.ItemID.StartsWith("090")).Sum(h =>h.ProcessDuration)): g.FirstOrDefault(w =>w.TimeSheetCategory =="FN").HoursWorked,
RmGrHrs =g.Where(w =>w.TimeSheetCategory == "RM").Sum(h => h.HoursProduced),RmGrHrsNet =g.Where(w =>w.TimeSheetCategory == "RM").Sum(h => h.HoursProducedNet),
RmTlHrs =g.FirstOrDefault(w =>w.TimeSheetCategory == "RM").HoursWorked,
MpGrHrs =g.Where(w =>w.TimeSheetCategory =="MATPREP").Sum(h => h.HoursProduced),
MpTlHrs =g.FirstOrDefault(w =>w.TimeSheetCategory =="MATPREP").HoursWorked,
DtHrs = g.Sum(s => s.DowntimeHours),
Indirect =g.Where(w =>w.ItemID.StartsWith("090")).Sum(h => h.ProcessDuration)
});
return query.ToList();
}
The first few bits just gather the data, it's the last query that is the "meat" of the procedure and takes the time.
I'm fairly sure I've done something horrid as the SQL it spits out is terrible, but for the life of me i can't see how to improve it.
Any hints greatly appreciated.
Gordon
Your expression gets optimized both in IQueriable compilation and SQL server query optimization and even here takes that long. It's highly probable that you have no column indexes needed for execution plan to be faster. Copy/paste your rendered SQL expression to SSMS, run it and see the actual plan. Optimize database structire if needed (put indexes). Otherwise, you got that really large amont of data that makes process slow.
Related
I have a main table "SALES" and two secondary tables "PRODUCTS" and "SERVICES", I need to select only the records in "SALES" that contain some product or service entered by the user, I don't need to bring the sales records and products, just filter. First I made the filter in the table "SALES" by date of sale:
var query = (from p in _contexto.sales
where p.datesale.Value.Date >= Convert.ToDateTime(strDtI).Date &&
p.datesale.Value.Date <= Convert.ToDateTime(strDtF).Date
select p);
Now let's say the user wants to filter also the sales that have products or services with the words in a string Array
words = ['apple', 'beef', 'cleaning', 'haircut']
if you receive the array of words, I tried the filter below, but it didn't work, it kept bringing all the records.
var queryi = (from i in _contexto.products
where words.Contains(i.name) || words.Contains(i.description) select i);
//var queryj = (from i in _contexto.services
//where words.Contains(i.name) || words.Contains(i.description) select i);
//query = query.Where(p => queryi.All(c => c.idsale != p.id) || queryj.All(c => c.idsale != p.id));
query = query.Where(p => queryi.All(c => c.idsale != p.id));
where am I failing, and is there a better and more performant way to do this?
Thank you!
Using more descriptive variable names, and assuming you meant to only find products that have the exact same name or description as one of the words, you would have:
var salesInPeriod = from s in _contexto.sales
where Convert.ToDateTime(strDtI).Date <= s.datesale.Value.Date &&
s.datesale.Value.Date <= Convert.ToDateTime(strDtF).Date
select s;
var matchingidsales = from p in _contexto.products
where words.Contains(p.name) || words.Contains(p.description)
select p.idsale;
var ans = from s in salesInPeriod
where matchingidsales.Contains(s.id)
select s;
PS: I inverted the date comparison since I think it makes it easier to see you are doing a between test.
I'm currently creating a site that showcases all my patients within a data table and I have to use FromSqlRaw in order to get the data from my database. I have a search funtion that allows me to search the patients within the table but upon entering the page I get this error when I use AsQueryable and no data is displayed in the table. It recommends me to use AsEnumerable but when I do I get an intellisense error. Any ideas on how to fix?
public async Task<IActionResult> Search(StaySearchViewModel model)
{
if (model.Cleared)
{
return Json(new
{
draw = model.Draw,
data = new object[] { },
recordsFiltered = 0,
recordsTotal = 0,
total = 0
});
}
var records = getSearchData(model);
//var records = System.Linq.Enumerable.AsEnumerable(getSearchData(model)); // Hard coding this an enumerable will break line 55, 57, and 64
//Sorting
if (!string.IsNullOrEmpty(model.SortOrder))
records = records.OrderBy(model.SortOrder);
var count = await records.CountAsync().ConfigureAwait(false);
records = records.Skip(model.Start);
if (model.Length != -1) records = records.Take(model.Length);
// Create models
var result = new List<SpStaySearchResultViewModel>();
try
{
await records.ForEachAsync(r =>
{
result.Add(new SpStaySearchResultViewModel()
{
BuildingName = r.BuildingName,
CaseManager = r.CaseManager,
CaseManagerId = r.CaseManagerId,
OccupantFileAs = r.OccupantFileAs,
StayOCFSNumber = r.StayOCFSNumber,
StayId = r.StayId,
MaxOfBillSentDate = r.MaxOfBillSentDate,
CountOfChildren = r.CountOfChildren,
StartDate = r.StartDate,
EndDate = r.EndDate,
OccupantId = r.OccupantId,
IsActive = r.IsActive,
});
}).ConfigureAwait(false);
}
catch (Exception e) { }
return Json(new
{
draw = model.Draw,
data = result,
recordsFiltered = count,
recordsTotal = count,
});
}
private IQueryable<spStaysSearch> getSearchData(StaySearchViewModel model)
{
var records = db.SpStaySearches.FromSqlRaw("dbo.spStaysSearch").AsQueryable();
if (model.OccupantId.HasValue)
records = records.Where(x => x.OccupantId == model.OccupantId);
if (!string.IsNullOrWhiteSpace(model.OccupantFileAs))
records = records.Where(x => x.OccupantFileAs == model.OccupantFileAs);
if (!string.IsNullOrWhiteSpace(model.BuildingName))
records = records.Where(x => x.BuildingName == model.BuildingName);
if (!string.IsNullOrWhiteSpace(model.CaseManager))
records = records.Where(x => x.CaseManager == model.CaseManager);
if (!string.IsNullOrWhiteSpace(model.BuildingName))
records = records.Where(x => x.BuildingName == model.BuildingName);
if (model.IntakeDateStart.HasValue && model.IntakeDateEnd.HasValue)
{
records = records.Where(x => x.StartDate >= model.IntakeDateStart && x.StartDate <= model.IntakeDateEnd);
}
else
{
if (model.IntakeDateStart.HasValue)
records = records.Where(x => x.StartDate >= model.IntakeDateStart);
if (model.IntakeDateEnd.HasValue)
records = records.Where(x => x.StartDate <= model.IntakeDateEnd);
}
if (model.ExitDateStart.HasValue && model.ExitDateEnd.HasValue)
{
records = records.Where(x => x.EndDate >= model.ExitDateStart && x.EndDate <= model.ExitDateEnd);
}
else
{
if (model.ExitDateStart.HasValue)
records = records.Where(x => x.EndDate >= model.ExitDateStart);
if (model.ExitDateEnd.HasValue)
records = records.Where(x => x.EndDate <= model.ExitDateEnd);
}
if (model.IsActive.HasValue)
records = records.Where(x => x.IsActive == model.IsActive);
return records;
}
Try this
var records = getSearchData(model).ToList();
var count = records.Count;
.....
You can't order records by model.SortOrder since it has nothing to do with records.
You can only do something like this
if (!string.IsNullOrEmpty(model.SortOrder)) records = records.OrderBy(r=> r.Id);
because your source data is a Stored Procedure, you cannot compose additional query expressions over the top of it. Instead you must load it into memory, as the error suggests, by enumerating the result set.
Including Related Data
SQL Server doesn't allow composing over stored procedure calls, so any attempt to apply additional query operators to such a call will result in invalid SQL. Use AsEnumerable or AsAsyncEnumerable method right after FromSqlRaw or FromSqlInterpolated methods to make sure that EF Core doesn't try to compose over a stored procedure.
The obvious way to interpret this in the code is to call .ToList() on the results from the SP, then to match your existing code pattern you can cast that result back to IQueryable:
var records = db.SpStaySearches.FromSqlRaw("dbo.spStaysSearch")
.ToList()
.AsQueryable()
Using AsEnumerable() is sometimes problematic as there are many different libraries that you may have implemented that might all provide an AsEnumerable() extension method.
We have to do this because even in SQL you cannot simply select from an SP and then add where clauses to it, you first have to read the results into a temporary table or a table variable, then you can re-query from the result set, that is what we are effectively doing now, we are reading the results into a C# variable (.ToList()) and then composing an in-memory query over the top of that result.
If your search logic must be encapsulated in a stored procedure, then given the technical limitations, the usual expectation is that you would add the search arguments as optional parameters to the stored procedure, rather then tring to add filter clauses on top of the results in C#.
We can help with how to move your filter logic into dbo.spStaysSearch but you'll have to post the content of that SP, ideally as a new question.
Instead of using an SP at all, where we lose practically all the goodness that EF can offer us, an alternative approach is to replace your SP entirely with a raw SQL then the rest of your logic will work as expected.
var sql = #"
SELECT
tblStays.*, tblOccupant.OccupantID,
tblOccupant.FileAs AS OccupantFileAs,
IIF(tblStays.BuildingName LIKE 'Main Shelter',
tblOccupant.OCFSMainNumber,
tblOccupant.OCFSNorthNumber) AS StayOCFSNumber,
COALESCE([CountOfOccupantStayID], 0) AS CountOfChildren,
tblCaseManager.FileAs AS CaseManager,
StaysMaxBillSentDate.MaxOfBillSentDate
FROM tblStays
LEFT JOIN tblOccupantStays ON tblStays.StayID = tblOccupantStays.StayID
LEFT JOIN tblOccupant ON tblOccupantStays.OccupantID = tblOccupant.OccupantID
LEFT JOIN (
SELECT lkpOccStays.StayID
, COUNT(tblOccupantStays.OccupantStayID) AS CountOfOccupantStayID
FROM tblOccupantStays lkpOccStays
INNER JOIN tblOccupant lkpChild ON lkpOccStays.OccupantID = lkpChild.OccupantID
WHERE lkpChild.OccupantType LIKE 'Child'
GROUP BY lkpOccStays.StayID
) OccupantStays_CountOfChildren ON tblStays.StayID = OccupantStays_CountOfChildren.StayID
LEFT JOIN tblCaseManager ON tblStays.CaseManagerID = tblCaseManager.CaseManagerID
LEFT JOIN (SELECT tblStayBillingHx.StayID
, MAX(tblStayBillingHx.BillSentDate) AS MaxOfBillSentDate
FROM tblStayBillingHx
GROUP BY tblStayBillingHx.StayID
) StaysMaxBillSentDate ON tblStays.StayID = StaysMaxBillSentDate.StayID
";
var records = db.SpStaySearches.FromSqlRaw(sql);
In this way the SP is providing the structure of the resultset, which might be necessary if you are using the Database-First approach but you are no longer executing the SP at all.
The SQL in this answer is provided as a guide to the syntax only, there is not enough information available to determine the validity of the query or that the results conform to your business requirements.
SqlServer table contains records of time and hours burned, materials cost by vendors
[IdPK] [DateTime] [MaterialsCost] [Hours] [Vendor_FK] [Project_FK] [Lat] [Long]
The user decides how far back he wants to see totals and for which column value, i.e. he wants totals going back - X number of monthS, or X number of weekS on any columns_FK filter value.
For e.g. he wants totals for cost, hours on either, Vendor = Nike or ProjectX, going back (1) month, (3) weeks, or 2 months from a certain date. So, I'm trying to get the totals for based on a parameterized Linq query.
Question:
Using any of columns as a filter/selector in linq, how to get the Monthly/weekly totals for cost,hours - going back (X) number of months or weeks?
Also, should I write separate queries for months vs weeks, and separate queries for each column (columns value selected by the user, is passed to me as a genric val)?
// I tried this... but just stuck
DateTime now = DateTime.Now;
Int goBack = passedinGoingBack; // amount of units to go back
var backUnits = obj.GetType().GetProperty(name); // tried getting month or week??
var getRows = table.AsEnumerable()
var columnName = passedInColumnName;
var filter = passedInValue;
.Where(r => r.Field<DateTime>(columnName).Year == now.Year
&& r.Field<DateTime>(columnName).Month == now.Month);
This question is a little bit opinion-based. You can do it in any way it is convenient for you. That's how I would do this.
First: you can use Sum LINQ function for getting total according to the given summing rule.
var monthlyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName").Year == now.Year
&& r.Field<DateTime>("ColumnName").Month == now.Month);
var monthlyTotalForCost = monthlyRows.Sum(r => r.Field<decimal>("CostColumn"));
var monthlyTotalForHours = monthlyRows.Sum(r => r.Field<decimal>("HoursColumn"));
var weeklyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName").Year == now.Year
&& r.Field<DateTime>("ColumnName").Week == now.Week);
var weeklyTotalForCost = monthlyRows.Sum(r => r.Field<decimal>("CostColumn"));
var weeklyTotalForHours = monthlyRows.Sum(r => r.Field<decimal>("HoursColumn"));
Second: I would do separate queries for weekly and monthly totals as that' a sum of different entity values and there can be a different logic.
However, I would do a helping function for me like this:
public static class TableLinqHelper
{
public static SumOfTableColumn<T>(this IEnumerable<DataRow> rows, string columnName)
{
return rows.Sum(r => r.Field<T>(columnName));
}
public static DateTime GetDate(this DataRow row)
{
return row.Field<DateTime>("DateColumn");
}
public static GetTotalForCost(this IEnumerable<DataRow> row)
{
return SumOfTableColumn<decimal>(row, "CostColumn");
}
public static GetTotalForHours(this IEnumerable<DataRow> row)
{
return SumOfTableColumn<double>(row, "HoursColumn");
}
}
var monthlyRows = table.AsEnumerable()
.Where(r => r.GetTime().Year == now.Year
&& r.GetTime().Month == now.Month);
var weeklyRows = table.AsEnumerable()
.Where(r => r.GetTime().Year == now.Year
&& r.GetTime().Week == now.Week);
var monthlyTotalForCost = monthlyRows.GetTotalForCost();
var monthlyTotalForHours = monthlyRows.GetTotalForHours();
var weeklyTotalForCost = weeklyRows.GetTotalForCost();
var weeklyTotalForHours = weeklyRows.GetTotalForHours();
Update:
Filtering: You can filter your results using Where LINQ.
Dictionary<string, object> Filters = new Dictionary<string, object>();
Filters.Add("VendorColumn", "Nike");
Filters.Add("Hours", 7.0);
foreach (var filter in Filters)
{
monthlyRows = monthlyRows.Where(r => // ...);
}
Several monthes and weeks: You can change the condition of DateTime where.
int rowsToBeShown = 4;
var monthlyRows = table.AsEnumerable()
.Where(r => r.Field<DateTime>("ColumnName") > DateTime.Now.AddMonths(-rowsToBeShown));
It will show results for the last 4 monthes.
LINQ provides very convenient and flexible tools for data manipulation. It's all up to your fantasy.
I have to retrieve stocks and recent 4 vendors. I got stock in one list. Got purchase order with vendors in another list. Want to get a merge result of all stocks with their recent 4 vendors in purchasing if any. I am not good with LINQ and this is NHibernate as well
This is my updated work. Seeking how to write third LINQ sql to get this.
IEnumerable<StockDetailReportModal> stockDetail = Session.Query<Stock>()
.Where(predicate)
.ToList()
.Select(n => new StockDetailReportModal
{
Id = n.Id,
Number = n.Number,
PaddedNumber = n.PaddedNumber,
Buyer = n.Buyer == null ? string.Empty : n.Buyer.FullName,
SalesTaxCode = n.SalesTaxCode == null ? null : n.SalesTaxCode.Code,
PurchasingUnitOfMeasure = n.PurchasingUnitOfMeasure,
InventoryUnitOfMeasure = n.InventoryUnitOfMeasure,
CatalogueDescription = n.CatalogueDescription,
BrandDescription = n.BrandDescription,
EconomicOrderQuantity = n.EconomicOrderQuantity,
LastYearPurchasePrice = n.LastYearPurchasePrice,
ThisYearIssuePrice = n.ThisYearIssuePrice,
NextYearIssuePrice = n.NextYearIssuePrice,
Description = n.StockCommodityCode == null ? string.Empty : n.StockCommodityCode.Code + "-" + n.StockCommodityCode.Description,
}).OrderBy(o => o.VendorNumber);
var vendorhistoryList = Session.Query<PurchaseOrderLineItem>()
.Where(pOpredicate)
.Where(p => p.Stock.Number != null)
.ToList()
.Select(c => new
{
stockId = c.Id,
LastPurchaseDate = c.PurchaseOrder.OrderDate.ToString("yyyy/MM/dd"),
VendorNumber = c.PurchaseOrder.PurchaseOrderVendor.Vendor.Number,
LastPurchasePrice = c.UnitPrice,
LastTransactionDate =
c.LastTransactionDate != null ? c.LastTransactionDate.Value.ToString("yyyy/MM/dd") : null,
LeadTimeDays = GetDays(c.PurchaseOrder.OrderDate, c.LastTransactionDate),
}).GroupBy(d => d.VendorNumber).Select(gpo => gpo
.OrderByDescending(x => x.LastPurchaseDate)
.Take(4));
My problem starts here when vendorhistoryList object does not show any property. What I am doing wrong here? I am trying to left join stockDetail with vendorhistoryList on stock.id with 1 or more stock rows with upto 4 recent vendors. Please suggest what should I do?
Here is the last part I need.
var stockdetailResult = from sd in stockDetail
join vh in vendorhistoryList on sd.Id equals vh.Id into sv
from vs in sv.DefaultIfEmpty() ???
.select ( c=> c.fields1....c.fields2...)
This was a very similar problem I was facing so just answered to that question which can be applied here as well. Attaching a link of that question.
Product with last 4 vendors on transaction date
I am wondering if there is a better, more efficient way to re-code the linq syntax below to make the query run faster i.e. with a single call to the database. My database is located remotely which causes this to be quite slow:
var query = (from ticket in dataClassesDataContext.Tickets.Where(TicketsToShow.And(SearchVals))
select new
{
Priority = ticket.TicketPriority.TicketPriorityName,
Ticket = string.Format(TicketFormat, ticket.TicketID),
AssetId = ticket.Asset.Serial,
OpenDate = ticket.CheckedInDate,
OpenFor = CalculateOpenDaysAndHours(ticket.CheckedInDate, ticket.ClosedDate),
Account = ticket.Account.Customer.Name,
Description = ticket.Description.Replace("\n", ", "),
Status = ticket.TicketStatus.TicketStatusName,
Closed = ticket.ClosedDate,
THIS IS THE CAUSE ====>>> Amount = GetOutstandingBalanceForTicket(ticket.TicketID),
Paid = ticket.Paid,
Warranty = ticket.WarrantyRepair,
AssetLocation = GetAssetLocationNameFromID(ticket.Asset.LocationID, AssLocNames)
}).Skip(totalToDisplay * page).Take(totalToDisplay);
if (SortOrder.ToLower().Contains("Asc".ToLower()))
{
query = query.OrderBy(p => p.OpenDate);
}
else
{
query = query.OrderByDescending(p => p.OpenDate);
}//ENDIF
The main cause for the poor performance is the code in the function GetOutstandingBalanceForTicket below which calculates the sum of all items in an invoice and returns this as a total in a string:
public static string GetOutstandingBalanceForTicket(int TicketID)
{
string result = string.Empty;
decimal total = 0;
try
{
using (DataClassesDataContext dataClassesDataContext = new DataClassesDataContext(cDbConnection.GetConnectionString()))
{
var queryCustomerTickets = from ticket in dataClassesDataContext.Tickets
where
(ticket.TicketID == TicketID)
select ticket;
if (queryCustomerTickets != null)
{
foreach (var ticket in queryCustomerTickets)
{
var queryTicketChargeItems = from chargeItem in dataClassesDataContext.ProductChargeItems
where chargeItem.ChargeID == ticket.ChargeID &&
chargeItem.Deleted == null
select chargeItem;
foreach (var chargeItem in queryTicketChargeItems)
{
total += (chargeItem.Qty * chargeItem.Price);
}
}
}
}
}
catch (Exception ex)
{
}
return total.ToString("0.##");
}
Thank you in advance.
As you pointed out this code is quite slow as a query will be required for each ticket.
to eliminate the need for multiple queries you should look at applying an inner join between the ticketsToShow and the tickets entity (on the ticketid), using groupby to provide the sum of the charges for each ticket.
This is well illustrated in the answers to LINQ: Using INNER JOIN, Group and SUM
Ideally you would probably approach it more as an eager loading all at once type of setup. However, I do not think linq2sql supports that (I know EF does). One thing you can do is avoid the nested query though. Since you already have access to the ticket table, perhaps you should just issue a Sum() on it from your select statement. Hard for me to verify if any of this is an improvement so this code is kind of on the fly if you will.
//(from ticket in dataClassesDataContext.Tickets.Where(TicketsToShow.And(SearchVals))
(from ticket in dataClassesDataContext.Tickets
//this would be where you could eager load if possible (not entirely required)
//.Include is an EF method used only as example
/*.Include(t => t.TicketPriority)//eager load required entities
.Include(t => t.Asset)//eager load required entities
.Include(t => t.Account.Customer)//eager load required entities
.Include(t => t.TicketStatus)//eager load required entities
.Include(t => t.ProductChargeItems)//eager load required entities
*/
.Where(TicketsToShow.And(SearchVals))
select new
{
Priority = ticket.TicketPriority.TicketPriorityName,
Ticket = string.Format(TicketFormat, ticket.TicketID),
AssetId = ticket.Asset.Serial,
OpenDate = ticket.CheckedInDate,
OpenFor = CalculateOpenDaysAndHours(ticket.CheckedInDate, ticket.ClosedDate),
Account = ticket.Account.Customer.Name,
Description = ticket.Description.Replace("\n", ", "),
Status = ticket.TicketStatus.TicketStatusName,
Closed = ticket.ClosedDate,
//Use Sum and the foreign relation instead of a nested query
Amount = ticket.ProductChargeItems.Where(pci => pci.Deleted == null).Sum(pci => pci.Qty * pci.Price),
Paid = ticket.Paid,
Warranty = ticket.WarrantyRepair,
AssetLocation = GetAssetLocationNameFromID(ticket.Asset.LocationID, AssLocNames)
}).Skip(totalToDisplay * page).Take(totalToDisplay);
if (SortOrder.ToLower().Contains("Asc".ToLower()))
{
query = query.OrderBy(p => p.OpenDate);
}
else
{
query = query.OrderByDescending(p => p.OpenDate);
}
I think, you can make this query simplier. Somethink like this:
public static string GetOutstandingBalanceForTicket(DataClassesDataContext context, int TicketID)
{
decimal total = 0;
var total = (from ticket in context.Tickets
join chargeItem from context.ProductChargeItems on chargeItem.ChargeID == ticket.ChargeID
where (ticket.TicketID == TicketID && chargeItem.Deleted == null)
select chargeItem).Sum(chargeItem => chargeItem.Qty * chargeItem.Price);
return total.ToString("0.##");
}
/*...*/
Amount = GetOutstandingBalanceForTicket(dataClassesDataContext, ticket.TicketID),
Now, you can inline this methos in your query.
It can contains syntax errors, because I wrote it in notepad.