The following C# Linq example displays the correct records in a serverSide datatable. However, when I add the following line: data = data.Concat(unconfirmed);, it increases the page/datatable load time from ~500 ms to ~2 seconds when year equals the current year. Searching by the current year displays the most amount of records, but they are only ~90. Searching by a previous year displays ~10 records at most and it only takes ~500 ms for the page/datatable to load:
var data = _db.ResidencyConfirmations.AsQueryable();
var year = Request.Form.GetValues("columns[11][search][value]")[0];
var showConfirmed = Request.Form.GetValues("columns[12][search][value]")[0];
int.TryParse(year, out var resultYear);
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
var allNotesReleased = data.Where(rc => rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Select(aap => aap.ActivityPhas.ActivityPhase)
.FirstOrDefault() == "All Notes Released" &&
rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Select(aap => aap.ActivityPhaseDate)
.FirstOrDefault().Year >= resultYear);
var complete = data.Where(rc => rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == rc.Application.ApplicationActivityPhas
.Max(x => x.ActivityPhas.WorkFlowStep))
.Select(aap => aap.ActivityPhas.ActivityPhase)
.FirstOrDefault() == "Complete" &&
rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 5)
.Select(aap => aap.ActivityPhaseDate)
.FirstOrDefault().Year < resultYear);
var unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear))
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
data = allNotesReleased.Concat(complete)
.Where(rc => rc.ResidencyConfirmationDate.Year == resultYear);
if (showConfirmed == "false")
data = data.Where(rc => !rc.Deed || !rc.Utility || !rc.VitalRecords);
data = data.Concat(unconfirmed);
Not sure how it compiles, written in text editor, but query should be faster. Note that instead of using .Year introduced range variables, it will speedup query if you have indexes on date.
var year = Request.Form.GetValues("columns[11][search][value]")[0];
var showConfirmed = Request.Form.GetValues("columns[12][search][value]")[0];
int.TryParse(year, out var resultYear);
var startOfTheYear = new DateTime(resultYear, 1, 1);
var nextYear = startOfTheYear.AddYears(1);
var data = _db.ResidencyConfirmations.AsQueryable();
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
var allNotesReleased =
from rc in data
from aap in rc.Application.ApplicationActivityPhas
.Where(aap => aap.ActivityPhas.WorkFlowStep == 6)
.Take(1)
where
aap.ActivityPhas.ActivityPhase == "All Notes Released" &&
app.ActivityPhaseDate >= startOfTheYear &&
rc.ResidencyConfirmationDate >= startOfTheYear && rc.ResidencyConfirmationDate < nextYear
select rc;
var complete =
from rc in data
from aap in rc.Application.ApplicationActivityPhas
.OrderByDescending(aap => aap.WorkFlowStep)
.Take(1)
from aap2 in rc.Application.ApplicationActivityPhas
.Where(aap2 => aap2.ActivityPhas.WorkFlowStep == 5)
.Take(1)
where
aap.ActivityPhas.ActivityPhase == "Complete" &&
app2.ActivityPhaseDate < startOfTheYear &&
rc.ResidencyConfirmationDate >= startOfTheYear && rc.ResidencyConfirmationDate < nextYear
select rc;
var unconfirmed = complete
.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate >= startOfTheYear && x.ResidencyConfirmationDate < nextYear)
)
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc
.OrderByDescending(x => x.ResidencyConfirmationDate)
.First()
);
if (showConfirmed == "false")
data = data.Where(rc => !rc.Deed || !rc.Utility || !rc.VitalRecords);
data = data.Concat(unconfirmed);
To improve performance and to display the correct records, I made the following changes/additions:
if (resultYear == DateTime.Now.Year)
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab")
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
else
data = data.Where(rc => rc.Application.ApplicationType.Label == "Home Rehab");
IQueryable<ResidencyConfirmation> unconfirmed = null;
if (resultYear == DateTime.Now.Year)
unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear));
else
unconfirmed = complete.Where(rc => !rc.Application.ResidencyConfirmations
.Any(x => x.ResidencyConfirmationDate.Year == resultYear))
.GroupBy(rc => rc.Application.AppNumber)
.Select(rc => rc.OrderByDescending(x => x.ResidencyConfirmationDate)
.FirstOrDefault());
It now takes right under 1 second for the page/datatable to load.
Related
I have the following working query:
posts.Where(post =>
post.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.Where(x => x.Value > 10 && x.Value < 40)
.Any()
)
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
I would like to replace Where(x => x.Value > 10 && x.Value < 40) so it looks up from a list of ranges:
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
My understanding is I can use select to iterate over the items:
posts.Where(post =>
suppliedTimes.Select(x => new {low = Convert.ToInt32(x.Split("-",StringSplitOptions.RemoveEmptyEntries)[0]), high = Convert.ToInt32(x.Split("-",StringSplitOptions.RemoveEmptyEntries)[1]) })
.Any( a =>
post.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.Where(x => x.Value > a.low && x.Value < a.high)
.Any()
)
)
However this code results in the error:
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Please can someone explain how I can achieve this and why what I have isn't working.
To make it work with EF Core I would suggest my extnsion FilterByItems and change the way how to retrieve records.
List<string> suppliedTimes = new List<string>(){
"10-60","0-10"
};
var ranges = suppliedTimes
.Select(x => x.Split("-", StringSplitOptions.RemoveEmptyEntries))
.Select(x => new {
low = Convert.ToInt32(x[0]),
high = Convert.ToInt32(x[1])
});
var fields = context.Fields
.Where(x =>
x.RegionId == "RecipeArticleDetails" &&
(x.FieldId == "RecipePrepTime" || x.FieldId == "RecipeCookTime")
)
.GroupBy(x => x.PostId)
.Select(x => new { ID = x.Key, Value = x.Sum(y => Convert.ToInt32(y.Value)) })
.FilterByItems(ranges, (e, r) => e.Value > r.low && e.Value < r.high, true);
var posts = posts
.Join(fields, p => p.Id, f => f.ID, (p, f) => p);
var _query = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date >= From.Date && x.CreationDate.Date <= To.Date && (x.Type == Common.TransactionType.CPV || x.Type == Common.TransactionType.BPV) && x.CompanyCode == BranchCode && x.DebitAmount > 0)
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
});
var balances = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode)
//.Exist(_query.Account) (I want only account which exist in upper query)
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
Balance = g.Sum(x => x.DebitAmount - x.CreditAmount),
});
I want to use something like EXIST here in first query I have some specific accounts and in 2nd query I want to calculate balances of those accounts.
Can you please tell how I can use Exist function in LINQ.
Thank you.
I want this query to be implemented in LINQ:
SELECT `c`.`AccountCode`, `c`.`CompanyCode` AS `LocationCode`, COALESCE(SUM(`c`.`DebitAmount` - `c`.`CreditAmount`), 0) AS `Balance`
FROM `CashVoucherDetails` AS `c`
WHERE (CONVERT(`c`.`CreationDate`, date) <= '2022-12-20') AND (`c`.`CompanyCode` = '002') and `c`.`AccountCode` IN
(
SELECT `c`.`AccountCode`
FROM `CashVoucherDetails` AS `c`
WHERE ((((CONVERT(`c`.`CreationDate`, date) >= '2022-12-20') AND (CONVERT(`c`.`CreationDate`, date) <= '2022-12-20')) AND `c`.`Type` IN ('CPV', 'BPV')) AND (`c`.`CompanyCode` = '002')) AND (`c`.`DebitAmount` > 0)
GROUP BY `c`.`AccountCode`
)
GROUP BY `c`.`AccountCode`, `c`.`CompanyCode`;
you can use Contains or Any.
_dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode)
.Where(x => _query.Select(q => q.AccountCode).Contains(x.AccountCode))
Or
.Where(x => _query.Any(q => q.AccountCode == x.AccountCode))
Maybe something like that
var balances = _dbContext.CashVoucherDetails
.Where(x => x.CreationDate.Date <= To.Date && x.CompanyCode == BranchCode && _query.Any(q => q.AccountCode == x.AccountCode))
.GroupBy(v => new { v.AccountCode})
.Select(g => new
{
AccountCode = g.Key.AccountCode,
Balance = g.Sum(x => x.DebitAmount - x.CreditAmount),
});
Using Entity Framework core, can I get the total sum of the column and row count in one call?
I have the following code, but I think there is a better way to do this.
TotalCostResponse result = new TotalCostResponse
{
TotalCost = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.SumAsync(x => x.Amount),
TotalNumber = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date = request.StartDate
&& x.Date <= request.EndDate)
.CountAsync()
};
So instead of calling dbContext two times, I need to make it in one call.
var result = await dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.GroupBy(x => 1)
.Select(group => new TotalCostResponse
{
TotalCost = group.Sum(x => x.Amount),
TotalNumber = group.Count()
})
.FirstOrDefaultAsync();
Here is an example with one query, there are certainly other ways that you can find.
// In the select get only what you need, in your case only the amount
var transactions =
await this.dbContext.Transaction
.Where(x => x.UserName == request.UserName
&& x.Date >= request.StartDate
&& x.Date <= request.EndDate)
.Select(y => new
{
Amount = y.Amount,
}).ToListAsync();
// Calculating the data
var result = new TotalCostResponse
{
TotalCost = transactions.Sum(x => x),
TotalNumber = transactions.Count(),
}
I have the following query, often have excution timeout errors, have any ideas for optimization.
Thank you!
queryResult = db.Products
.Where(x => !x.product_is_deleted)
.Where(x => x.product_name.Contains(keyword) || x.product_barcode.Contains(keyword) || x.ProductType.pro_type_name.Contains(keyword) || x.ProductType.pro_type_name.Contains(keyword))
.Select(x => new ProductView
{
product_id = x.product_id,
product_barcode = x.product_barcode,
product_name = x.product_name,
product_unit = x.product_unit,
product_size = x.product_size,
product_weight = x.product_weight,
product_type_id = x.product_type_id,
product_inventory = shelf == 0 ? (x.ImportProductDetails.Where(y => y.imp_pro_detail_product_id == x.product_id && y.ImportProduct.import_pro_warehouse_id == warehouse_id ).Select(y => y.imp_pro_detail_inventory).DefaultIfEmpty(0).Sum()) : (x.ImportProductDetails.Where(y => y.imp_pro_detail_product_id == x.product_id && y.ImportProduct.import_pro_warehouse_id == warehouse_id && y.imp_pro_detail_shelf_id == shelf).Select(y => y.imp_pro_detail_inventory).DefaultIfEmpty(0).Sum()),
product_opening_stock = x.product_opening_stock,
product_type_name = x.ProductType.pro_type_name,
product_price = x.ProductPrices.Where(y => !y.proprice_is_deleted).Where(y => y.proprice_applied_datetime <= DateTime.Now).OrderByDescending(y => y.proprice_applied_datetime).Select(y => y.proprice_price).FirstOrDefault(),
product_discount_type = x.ProductDiscounts.Where(y => !y.pro_discount_is_deleted && y.pro_discount_start_datetime <= datetimeNow && y.pro_discount_end_datetime >= datetimeNow).OrderByDescending(y => y.pro_discount_id).Select(y => y.pro_discount_type).FirstOrDefault(),
product_discount_value = x.ProductDiscounts.Where(y => !y.pro_discount_is_deleted && y.pro_discount_start_datetime <= datetimeNow && y.pro_discount_end_datetime >= datetimeNow).OrderByDescending(y => y.pro_discount_id).Select(y => y.pro_discount_value).FirstOrDefault()
//product_norm_name = x.ProductionNorms.Where(y => y.pro_norm_name == x.product_id && y.pro_norm_is_applied == true).Select(y => y.pro_norm_id).FirstOrDefault()
});
I have 2 DbContexts in my application, and need to do a join in 2 tables that are each one in a different DbContext; I get this error
value cannot be null. parameter name entitytype
When I try to join 2 tables of the same context, this error does not happen.
var VerificaExistenciaSinistro = sinistroContext.SnsAviso
.Join(
sinistroContext.SnsNumAviso,
sinistro => sinistro.NumApo,
aviso => aviso.NumApo,
(sinistro, aviso) => new {
sinistroV = sinistro,
avisoV = aviso })
.Where(c => c.sinistroV.CodItm == c.avisoV.CodItm &&
c.sinistroV.NumApo == c.avisoV.NumApo &&
c.sinistroV.NumAvs == c.avisoV.SeqNumAvs)
.Join(sgsContext.EmsEmissao,
sinistro1 => sinistro1.sinistroV.CodCtrtAvs,
emissao => emissao.CodCtrt,
(sinistro1, emissao) => new {
sinistroC = sinistro1,
emissaoC = emissao })
.Where(c => c.sinistroC.sinistroV.CodCtrtAvs == c.emissaoC.CodCtrt &&
c.emissaoC.CodEmis == c.sinistroC.avisoV.CodEms)
.Where(x => x.sinistroC.sinistroV.NumApo == apolice &&
x.emissaoC.StsEmis == emissao &&
x.emissaoC.NumEndosso ==endosso &&
x.sinistroC.sinistroV.CodItm == cod_itm &&
x.sinistroC.sinistroV.CodCbe == cbeCod)
.Select(x => x.sinistroC)
.ToList();
It generally occurs when u query data from two dbcontext using IQueryable
I encountered the same issue when I was using two dbContext. The solution which I found was. If u are using join between two tables then load any one table's data first to a variable/Object so that It becomes IEnumerable or IList and then do join with that variable/Object to the another table of another dbContext
Example
var FirstDbContextTable = sinistroContext.SnsAviso
.Join(
sinistroContext.SnsNumAviso,
sinistro => sinistro.NumApo,
aviso => aviso.NumApo,
(sinistro, aviso) => new {
sinistroV = sinistro,
avisoV = aviso })
.Where(c => c.sinistroV.CodItm == c.avisoV.CodItm &&
c.sinistroV.NumApo == c.avisoV.NumApo &&
c.sinistroV.NumAvs == c.avisoV.SeqNumAvs).ToList();
var result=FirstDbContextTable.Join(sgsContext.EmsEmissao,
sinistro1 => sinistro1.sinistroV.CodCtrtAvs,
emissao => emissao.CodCtrt,
(sinistro1, emissao) => new {
sinistroC = sinistro1,
emissaoC = emissao })
.Where(c => c.sinistroC.sinistroV.CodCtrtAvs == c.emissaoC.CodCtrt &&
c.emissaoC.CodEmis == c.sinistroC.avisoV.CodEms)
.Where(x => x.sinistroC.sinistroV.NumApo == apolice &&
x.emissaoC.StsEmis == emissao &&
x.emissaoC.NumEndosso ==endosso &&
x.sinistroC.sinistroV.CodItm == cod_itm &&
x.sinistroC.sinistroV.CodCbe == cbeCod)
.Select(x => x.sinistroC)
.ToList();