我遇到以下问题:在我们的数据库中,我们记录了帮助台门票,我们在门票下预订了几小时。这些之间是访问报告。所以它是:ticket => visitreport =>小时。
小时有一定的'种类',这不是由小时记录中的类型指示器确定的,而是通过检查一小时的各种属性来编译的。例如,有一个客户但不是服务时间的小时始终是发票时间。
我想要的最后一件事就是那些“种类”的定义在代码中无处不在。他们必须在一个地方。其次,我希望能够从各种小时集合中计算小时数。例如,具有特定日期和特定客户的折旧票证集合。或者所有注册都标记为“解决方案”。
我决定使用'分层'数据库访问方法。相同的功能可以为屏幕表示提供数据,也可以为.pdf中的报告提供数据。因此,第一步收集所有相关数据。这可以用于.pdf创建,也可以用于屏幕表示。在这种情况下,必须在第二步中对其进行分页和排序。这样我就不需要基本上使用相同数据的单独查询。
数据量可能很大,例如年份总数的创建。因此,第一步中的数据应该是可查询的,而不是可枚举的。为了确保即使我在结果中添加小时总和,我仍然可以查询,我做了以下功能:
public static decimal TreeHours(this IEnumerable<Uren> h, FactHourType ht)
{
IQueryable<Uren> hours = h.AsQueryable();
ParameterExpression pe = Expression.Parameter(typeof(Uren), "Uren");
Expression left = Expression.Property(pe, typeof(Uren).GetProperty("IsOsab"));
Expression right = Expression.Constant(true, typeof(Boolean));
Expression isOsab = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
left = Expression.Property(pe, typeof(Uren).GetProperty("IsKlant"));
right = Expression.Constant(true, typeof(Boolean));
Expression isCustomer = Expression.Equal(Expression.Convert(left, typeof(Boolean)), Expression.Convert(right, typeof(Boolean)));
Expression notOsab;
Expression notCustomer;
Expression final;
switch (ht)
{
case FactHourType.Invoice:
notOsab = Expression.Not(isOsab);
final = Expression.And(notOsab, isCustomer);
break;
case FactHourType.NotInvoice:
notOsab = Expression.Not(isOsab);
notCustomer = Expression.Not(isCustomer);
final = Expression.And(notOsab, notCustomer);
break;
case FactHourType.OSAB:
final = Expression.And(isOsab, isCustomer);
break;
case FactHourType.OsabInvoice:
final = Expression.Equal(isCustomer, Expression.Constant(true, typeof(Boolean)));
break;
case FactHourType.Total:
final = Expression.Constant(true, typeof(Boolean));
break;
default:
throw new Exception("");
}
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { hours.ElementType },
hours.Expression,
Expression.Lambda<Func<Uren, bool>>(final, new ParameterExpression[] { pe })
);
IQueryable<Uren> result = hours.Provider.CreateQuery<Uren>(whereCallExpression);
return result.Sum(u => u.Uren1);
}
这个函数背后的想法是它应该是可查询的,这样我就不会将数据的数量转换为可枚举的。
我设法保持可查询直到结束。在第1步中,我收集原始数据。在第2步中,我订购数据,然后将其分页。在步骤3中,数据被转换为JSon并发送到客户端。它总计数小时。
问题是:我为每张票的小时收到一个查询。这是数以百计的查询!那太多了......
我尝试了以下方法:
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Ticket>(t => t.Bezoekrapport);
options.LoadWith<Bezoekrapport>(b => b.Urens);
dc.LoadOptions = options;
Bezoekrapport简直就是“visitreport”的荷兰语。当我查看检索票证的查询时,我看到它加入了Bezoekrapport / visitreport,但没有加入它的小时数。
我使用的第二种方法是手动加入LINQ中的小时数,但这不起作用。
我必须做错事。这里最好的方法是什么?
以下代码片段是我检索数据的方式。在最后一步中调用strHours上的toList()时,我得到了一个充满疑问的问题。我已经尝试了两天来解决它但它只是不起作用......我的方法或函数TreeHours中的某些东西一定是错的。
第1步:
IQueryable<RelationHoursTicketItem> HoursByTicket =
from Ticket t in allTickets
let RemarkSolved = t.TicketOpmerkings.SingleOrDefault(tr => tr.IsOplossing)
let hours = t.Bezoekrapport.Urens.
Where(h =>
(dateFrom == null || h.Datum >= dateFrom)
&& (dateTo == null || h.Datum <= dateTo)
&& h.Uren1 > 0)
select new RelationHoursTicketItem
{
Date = t.DatumCreatie,
DateSolved = RemarkSolved == null ? (DateTime?)null : RemarkSolved.Datum,
Ticket = t,
Relatie = t.Relatie,
HoursOsab = hours.TreeHours(FactHourType.OSAB),
HoursInvoice = hours.TreeHours(FactHourType.Invoice),
HoursNonInvoice = hours.TreeHours(FactHourType.NotInvoice),
HoursOsabInvoice = hours.TreeHours(FactHourType.OsabInvoice),
TicketNr = t.Id,
TicketName = t.Titel,
TicketCategorie = t.TicketCategorie,
TicketPriority = t.TicketPrioriteit,
TicketRemark = RemarkSolved
};
第2步
sort = sort ?? "TicketNr";
IQueryable<RelationHoursTicketItem> hoursByTicket = GetRelationHours(relation, dateFrom, dateTo, withBranches);
IOrderedQueryable<RelationHoursTicketItem> orderedResults;
if (dir == "ASC")
{
orderedResults = hoursByTicket.OrderBy(sort);
}
else
{
orderedResults = hoursByTicket.OrderByDescending(sort);
}
IEnumerable<RelationHoursTicketItem> pagedResults = orderedResults.Skip(start ?? 0).Take(limit ?? 25);
records = hoursByTicket.Count();
return pagedResults;
第3步:
IEnumerable<RelationHoursTicketItem> hours = _hourReportService.GetRelationReportHours(relation, dateFrom, dateTo, metFilialen, start, limit, dir, sort, out records);
var strHours = hours.Select(h => new
{
h.TicketNr,
h.TicketName,
RelationName = h.Relatie.Naam,
h.Date,
TicketPriority = h.TicketPriority.Naam,
h.DateSolved,
TicketCategorie = h.TicketCategorie == null ? "" : h.TicketCategorie.Naam,
TicketRemark = h.TicketRemark == null ? "" : h.TicketRemark.Opmerking,
h.HoursOsab,
h.HoursInvoice,
h.HoursNonInvoice,
h.HoursOsabInvoice
});
答案 0 :(得分:1)
我不认为你的TreeHours
扩展方法可以一次性由LINQ转换为SQL。因此在行的每个构造函数的执行时进行评估,在这种情况下每行导致对数据库的4
调用。
我会简化您的LINQ查询,以便从SQL返回原始数据,使用简单的JOIN获取所有票证并在那里花费数小时。然后我会按内存类型对小时数进行分组和过滤。否则,如果确实需要在SQL中执行操作,请查看CompiledQuery.Compile
方法。这应该能够处理不在每行进行查询。我不确定你是否会获得switch
,但您可以使用?:
运算符对其进行转换。