我们有一个简单的计算引擎,它接收存储在数据库中的字符串(例如“(x + y)* 4”),从数据库中提取x和y的值,执行计算并保存结果到数据库。这似乎花了太长时间,我担心我已经陷入了Linq的陷阱。如果有办法改善这一点,请告诉我:
public Nullable<decimal> CalculateFormulaByYearDistrict(int formulaId, int fundingYearId, int districtId)
{
string formulaText = "";
decimal? retValue = null;
using (STARSEntities context = new STARSEntities())
{
var formulaItems = from fi in context.STARS_FormulaItem
where fi.FormulaId == formulaId
select fi;
STARS_Formula formula = formulaItems.FirstOrDefault().STARS_Formula;
formulaText = formula.FormulaText;
foreach (STARS_FormulaItem formulaItem in formulaItems)
{
int accountingItemId = formulaItem.AccountingItemId;
var itemValue = (from iv in context.AccountingItemValues
join di in context.STARS_DistrictInputData
on iv.DomainSpecificId equals di.DistrictInputDataId
where (di.DistrictId == districtId || di.DistrictId == -1) //District -1 is the invalid and universal district for coefficients
&& di.DomainYearReportingPeriod.FundingYearId == fundingYearId
&& iv.AccountingItemId == accountingItemId
select iv).SingleOrDefault();
//If no value exists for the requested Assessment Item Value, then force an error message into the formula text
//to be thrown during calculate.
if (itemValue != null)
formulaText = Regex.Replace(formulaText, @"\b" + formulaItem.ItemCode + @"\b", itemValue.Amount.ToString());
else
formulaText = Regex.Replace(formulaText, @"\b" + formulaItem.ItemCode + @"\b", "No Value Exists for " + formulaItem.ItemCode);
}
switch (formula.FormulaTypeId)
{
case (int)FormulaType.CALC:
retValue = Calculate(formulaText);
break;
case (int)FormulaType.EXPONENT:
// pull the number directly after e and and calculate the Math.Exp(value) and then push that into the calculation.
retValue = Calculate(ReplaceExponent(formulaText));
break;
case (int)FormulaType.IFTHEN:
// evaluate the If statements and pass any math to the calculator.
retValue = Calculate(EvaluateIf(formulaText));
break;
default:
break;
}
}
return retValue;
}
public bool CalculateAndSaveResults(DistrictDataCategory category, List<int> districtIds, int fundingYearId, int userId)
{
//Optimization Logic
DateTime startTime = DateTime.Now;
Debug.WriteLine("Starting Calculate and Save at:" + startTime.ToString());
using (STARSEntities context = new STARSEntities())
{
var formulas = from f in context.STARS_FormulaCategory
where f.DistrictDataCategoryId == (int)category
select f.STARS_Formula;
foreach (var districtId in districtIds)
{
Debug.WriteLine("District: " + districtId.ToString());
DateTime districtStartTime = DateTime.Now;
foreach (var formula in formulas)
{
var itemValue = (from iv in context.AccountingItemValues
join di in context.STARS_DistrictInputData
on iv.DomainSpecificId equals di.DistrictInputDataId
where (di.DistrictId == districtId)
&& di.DomainYearReportingPeriod.FundingYearId == fundingYearId
&& iv.AccountingItemId == formula.ResultAccountingItemId
select new { iv, di }).SingleOrDefault();
itemValue.iv.Amount = CalculateFormulaByYearDistrict(formula.FormulaId, fundingYearId, districtId);
//Update Actual Amount Record
itemValue.iv.LastUpdated = DateTime.Now;
itemValue.iv.UpdatedBy = userId;
//Update District Data Import Record
itemValue.di.LastUpdated = DateTime.Now;
itemValue.di.UpdatedBy = userId;
}
Debug.WriteLine("District Calculation took: " + ((TimeSpan)(DateTime.Now - districtStartTime)).ToString() + "for " + districtId.ToString());
}
context.SaveChanges();
}
Debug.WriteLine("Finished Calculate and Save at:" + ((TimeSpan)(DateTime.Now - startTime)).ToString());
return true;
}
如果您需要有关基础数据结构的任何信息,请告诉我。看起来很重要的事情是,在存储公式文本的公式表之间存在关联实体,以便我们可以对给定区域执行特定类型的所有计算。存储的实际值位于AccountingItemValue表中,但是有一个名为DistrictInputData的关联表,其中包含有关会计项目值的位置信息。
非常感谢。
答案 0 :(得分:1)
我首先要从更细微的层面分解方法和分析;找出导致性能下降的确切原因。
问题可能不是Linq,而是数据库中的问题 - 你有没有对你的数据库进行分析和优化?你有合理的索引,它们是由EF正在生成的sql使用的吗?
我没有看到您的linq查询有任何明显错误。
答案 1 :(得分:0)
永远不要低估循环内查询的强大功能。也许你最好的选择是从不同的方法来看待它并将一些循环查询拉出来。你有没有运行任何计时器,看看它在哪个时间最长?我愿意打赌它是foreach循环中的那些LINQ查询。
答案 2 :(得分:0)
Linq JOIN 操作将遍历整个数据库,然后在 ON 语句中“合并”结果。
然后它将遍历结果并按 WHERE 语句条件进行过滤。
所以如果:
context.AccountingItemValues = N
context.STARS_DistrictInputData = M
然后 join 操作会给你一个结果(让我们想一下SQL一会儿)表格大小为Max(M,N)(最差的情况)。
然后它将遍历整个结果表,并使用 WHERE 语句过滤结果。
所以你在整个数据库中循环了两次以上。并且JOIN操作不是线性的,因此您可以获得更多的迭代。
<强>提高强>:
在联接之前使用特定于表格的 where 条件,因此您可以在联接之前减小表格的大小。
那会给你
context.AccountingItemValues = numberOf(accountingItemId)
context.STARS_DistrictInputData = numberOf(fundingYearId)
然后在更小的表上完成连接操作。