Запрос базы данных и несохраненные изменения для проверки с Entity Framework 6 - PullRequest
0 голосов
/ 26 сентября 2018

У меня есть слой данных, использующий Entity Framework 6 База данных сначала .Одна из моих сущностей представляет промежуток времени - у него есть дата начала и дата окончания.

public class Range
{
    public Guid ID { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime EndDate { get; set; }
}

Я хочу добавить проверку, которая гарантирует, что у Range s никогда не будет перекрывающихся дат.Приложение будет сохранять добавленные, измененные и удаленные Range s одновременно.Так что я играл с переопределением ValidateEntity в моем DbContext.Вот что я закончил, а затем понял, что у меня все еще есть проблема:

protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items)
{
    var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());
    if (entityEntry.Entity is Range && (entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified))
    {
        Range range = entityEntry.Entity as Range;

        if (Ranges.Local.Any(p => range.EndDate >= p.StartDate && range.StartDate <= p.EndDate) ||
            Ranges.Any(p => range.EndDate >= p.StartDate && range.StartDate <= p.EndDate))
        {
            result.ValidationErrors.Add(
                    new System.Data.Entity.Validation.DbValidationError("EndDate", "Cannot overlap another range.")); //Could be StartDate's fault but we just need save to fail
        }
    }

    if (result.ValidationErrors.Count > 0)
    {
        return result;
    }
    else
    {
        return base.ValidateEntity(entityEntry, items);
    }
}

Проблема в том, что Ranges.Local не запрашивает базу данных, а Ranges, который запрашивает базу данных, не включаетнесохраненные изменения.

EG Если в моей базе данных у меня есть Range, начиная с 9/1 и заканчивая 9/8, то в памяти я изменил его, чтобы он начинался 8/1 и заканчивался 8/8, и у меня естьтакже добавлен новый Range, начиная с 9/6 и заканчивая 9/12, Ranges.Any(p => blockOut.EndDate >= p.StartDate && blockOut.StartDate <= p.EndDate) вернет true для добавленного Range, потому что текущий сохраненный Range перекрывается.Но это действительно допустимо, потому что в памяти эти даты были изменены, и при сохранении не будет совпадений.

Есть ли способ запросить комбинацию Local и базы данных.IE только запрашивает базу данных для записей, которые не находятся в Local?

EDIT:

Обновленный код, который, я думаю, работает в ответ на ответ Стива Пи ниже:

        //Check Local first because we may be able to invalidate without querying the DB
        if (BlockOutPeriods.Local.Any(p => blockOut.ID != p.ID && blockOut.EndDate >= p.StartDate && blockOut.StartDate <= p.EndDate))
        {
            result.ValidationErrors.Add(
                    new System.Data.Entity.Validation.DbValidationError("EndDate",
                    "Block out cannot overlap another block out."));
        }
        else 
        {
            var editedIDs = BlockOutPeriods.Local.Select(p => p.ID);

            //!Contains avoids reading & mapping all records to Range model objects - better?
            if (BlockOutPeriods.Any(p => blockOut.EndDate >= p.StartDate && blockOut.StartDate <= p.EndDate && !editedIDs.Contains(p.ID)))
            {
                result.ValidationErrors.Add(
                        new System.Data.Entity.Validation.DbValidationError("EndDate",
                        "Block out cannot overlap another block out."));
            }
        }

1 Ответ

0 голосов
/ 26 сентября 2018

Вы можете использовать Союз между локальным состоянием и состоянием данных, предоставляя IEqualityComparer для сопоставления с PK, чтобы локальный использовался без дублирования из набора данных.

var result = Ranges.Local.Where(p => range.EndDate >= p.StartDate || range.StartDate <= p.EndDate)
  .Union(Ranges.Where(p => range.EndDate >= p.StartDate 
    || range.StartDate <= p.EndDate), new RangeEqualityComparer())
  .Any( p => p.RangeId != range.RangeId 
    && range.EndDate >= p.StartDate && range.StartDate <= p.EndDate);

Это должно:

  • извлекает любые диапазоны из Local, которые потенциально охватывают желаемую дату начала / окончания.
  • объединяет любые диапазоны из DB, которые потенциально охватывают требуемый диапазон дат.
  • проверка на наличиелюбой другой записи (<> мой идентификатор), которая охватывает диапазон.
...