我有一个使用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."));
}
}
答案 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);
这应该: