查询数据库和未保存的更改以使用Entity Framework 6进行验证

时间:2018-09-25 22:44:55

标签: entity-framework validation entity-framework-6

我有一个使用Entity Framework 6 Database First 的数据层。我的一个实体代表一个时间跨度-它有一个开始日期和一个结束日期。

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

我想添加验证以确保Range永远不会有重叠的日期。该应用程序将一次保存所有添加,修改和删除的Range。因此,我在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不包括未保存的更改。

E.G。如果我的数据库中有一个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)将为添加的Range返回true,因为当前保存的Range重叠。但这实际上是有效的,因为在内存中,这些日期已更改,并且保存时不会重叠。

有什么方法可以查询Local和数据库的组合。即只查询数据库中不在本地的记录?

编辑:

我认为对以下Steve Py的回答有效的更新代码:

        //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 :(得分:1)

您可以在本地状态和数据状态之间使用联合,提供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);

这应该:

  • 从“本地”中拉出可能跨越所需开始/结束日期的所有范围。
  • 具有数据库中可能跨越所需日期范围的任何范围的联盟。
  • 检查是否存在覆盖该范围的任何其他记录(<>我的ID)。