代码之家  ›  专栏  ›  技术社区  ›  xr280xr

查询数据库和未保存的更改以使用实体框架6进行验证

  •  0
  • xr280xr  · 技术社区  · 6 年前

    我有一个使用实体框架6的数据层 数据库优先 . 我的一个实体表示一个时间跨度——它有一个开始日期和一个结束日期。

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

    我想添加验证以确保 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 查询数据库的,不包括未保存的更改。

    例如,如果在我的数据库中 范围 从9/1开始到9/8结束,然后在内存中我将其修改为从8/1开始到8/8结束,并且我还添加了一个新的 范围 从9/6开始到9/12结束, Ranges.Any(p => blockOut.EndDate >= p.StartDate && blockOut.StartDate <= p.EndDate) 将为添加的返回true 范围 因为当前保存的 范围 重叠。但它实际上是有效的,因为在内存中,这些日期已经更改,并且在保存时不会有重叠。

    是否有任何方法可以查询本地和数据库的组合。即只查询数据库中不在本地的记录?

    编辑:

    更新后的代码,我认为是对史蒂夫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 回复  |  直到 6 年前
        1
  •  1
  •   Steve Py    6 年前

    您可以在本地状态和数据状态之间使用联合,提供一个IEqualityComparer来匹配pks,这样就可以在不复制数据集的情况下使用本地。

    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);
    

    这应该:

    • 从本地拉任何可能跨越所需开始/结束日期的范围。
    • 与可能跨越所需日期范围的db范围合并。
    • 检查是否存在覆盖范围的任何其他记录(<>我的ID)。