Entity Framework объединяет несколько запросов в один - PullRequest
0 голосов
/ 05 октября 2018

У меня есть запрос, который нужно выполнить 300+ циклов при каждом вызове.Требуется около 10 секунд, чтобы завершить вызов даже в новой базе данных.Это неприемлемо для вызова WebAPI.

var isAbnormal = false;
var list = new List<String>();

//Check date range, log them & return if there is any abnormal.
foreach (DateTime day in DateHelper.EachDay(startDate, endDate))
{
  var isActive = db.Operations.Any(x=>x.IsActive && x.Day == day);
  var object;
  var queryable = db.ObjectA.Where(x=>x.Day == day);

  if(isActive){
    queryable = db.ObjectA.First(x=>x.Day == day);

  LogUtil.Info($"{object.Name}");

  var isLogicACorrect = queryable.Any(x=>x.ObjectACount == 5);
  var isLogicBCorrect = queryable.Any(x=>x.ObjectBCount == 3);
  var isLogicCCorrect = queryable.Any(x=>x.ObjectCCount == 2);
  var isLogicDCorrect = queryable.Any(x=>x.ObjectDCount == 8);
  var isLogicECorrect = queryable.Any(x=>x.ObjectECount == 1);
  if(!isLogicACorrect){
    list.Add("Logic A is incorrect");
    isAbnormal = true;
    }

  //More logic codes & db calls here, which is just to select & validate.

}
return list;

Как оптимизировать скорость, объединив все запросы в один?Содержимое цикла буквально одинаково, за исключением дня. Всего в каждом цикле должно быть вызвано 15 запросов, в полном цикле - 4500 дБ.

1 Ответ

0 голосов
/ 05 октября 2018

Думайте в терминах наборов и реляционных данных, а не процедурно.Нелегко точно определить, что вы хотите, из вашего кода (что противоречит самому себе - queryable устанавливается вызовом db.ObjectA.Where(...), который является IQueryable<ObjectA>, но затем он также устанавливается вызовом db.ObjectA.First(...), которыйявляется ObjectA; я предполагаю, что вы хотите IQueryable<ObjectA>, поскольку более поздние коды ссылаются на queryable.Any(...)), но вот мое предположение:

var days = DateHelper.EachDay( startDate, endDate );

var activeDaysIsLogicCorrectFlags = db.Operations
    // get days that are "active"
    .Where( op => op.IsActive && days.Contains( op.Day ) )
    // join with ObjectA's to filter for active ObjectA's
    // is there a nav property you could use instead?
    // use GroupJoin for use with `Any(...)` in results
    .GroupJoin( db.ObjectA, op => op.Day, oa => oa.Day, ( op, oaGroup ) => new
        {
            //Operation = op,
            // projecting Operation.Day since that's what your foreach loop is using
            Day = op.Day,
            IsLogicACorrect = oaGroup.Any( oa => oa.ObjectACount == 5 ),
            // if IsLogicBCorrect can be determined from the collection of ObjectA's:
            //IsLogicBCorrect = oaGroup.Any( oa => oa.ObjectBCount == 3 ),
        } );

Результатом является IQueryable анонимного типа, которыйсопоставляет "активный" Operation.Day с вашей логикой для IsLogicACorrect.Для остальных ваших флагов IsLogicXCorrect, если все они могут быть определены с помощью ObjecetA group aoGroup, просто добавьте их в селектор результатов GroupJoin (как показано в закомментированном свойстве).Если эти флаги нуждаются в собственных группировках (например, для определения IsLogicBCorrect необходимо использовать группу ObjectB, затем добавьте дополнительные вызовы к GroupJoin, как показано выше, но с использованием их соответствующих DbSet и свойств. Например, если вам нужноиспользуйте db.ObjectB для IsLogicBCorrect:

var activeDaysIsLogicCorrectFlags =
    <existing logic from above>
    .GroupJoin( db.ObjectB, at => at.Day, ob => ob.Day, ( at, obGroup ) => new
        {
            // project all previous results
            at.Day,
            at.IsLogicACorrect,
            // new flag
            IsLogicBCorrecet = obGroup.Any( ob => ob.ObjectBCount == 3 ),
        } );
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...