У меня следующая проблема: В нашей базе данных мы записываем билеты в службу поддержки и бронируем часы по билетам. Между ними отчет о посещении. Так и есть: ticket => visitreport => hours.
Часы имеют определенный «вид», который не определяется индикатором типа в записи часа, но компилируется путем проверки различных свойств часа. Например, час, который имеет клиента, но не является часом обслуживания, всегда является часом счета.
Последнее, что я хочу, это то, что определения этих «видов» бродят повсюду в коде. Они должны быть в одном месте. Во-вторых, я хочу иметь возможность рассчитать общее количество часов из различных коллекций часов. Например, сплющенная коллекция билетов с определенной датой и определенным клиентом. Или все регистрации, которые помечены как «решение».
Я решил использовать «многоуровневый» подход к доступу к базе данных. Те же функции могут предоставлять данные для представления на экране, а также для отчета в формате .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 - просто голландский, что означает «посещение». Когда я смотрю на запрос, который получает билеты, я вижу, что он присоединяется к отчету Bezoekrapport / visitreport, но не к часам, которые к нему привязаны.
Второй подход, который я использовал, - это ручное объединение часов в LINQ, но это также не работает.
Я должен сделать что-то не так. Каков наилучший подход здесь?
Следующие фрагменты кода показывают, как я получаю данные. После вызова toList () для strHours на последнем шаге я получаю град запросов. Два дня я пытался обойти это, но это просто не работает ... Что-то не так в моем подходе или в функции 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
});