entity-framework - 查询数据库和未保存的更改以使用 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
s 永远不会有重叠的日期。该应用程序将一次保存添加、修改和删除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
查询数据库的 不包括未保存的更改。
例如:如果我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和数据库的组合,即只查询数据库中没有Local的记录?
编辑:
更新了我认为可以响应以下史蒂夫·皮的回答的代码:
//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."));
}
}
解决方案
您可以Union
在本地状态和数据状态之间使用 a,提供IEqualityComparer
匹配 PK 的 a,以便使用本地而不复制数据集。
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
拉出可能跨越所需开始/结束日期的任何范围。- 与数据库中可能跨越所需日期范围的任何范围联合。
- 检查是否存在
<>
涵盖该范围的任何其他记录(我的 ID)。
推荐阅读
- c# - 将带有命名空间的 XML 转换为 C# 模型类
- python - 如何使用 moto,@mock_dynamodb2 模拟失败的操作?
- django - 强制 Django 将模型字段值识别为模板标签值?
- c++ - 提升二进制反序列化
- vue.js - 如何使 Vue.js 3 中动态生成的按钮不执行相同的功能
- sqlalchemy - FastAPI / SQLAlchemy 创建多个用户
- filter - 获取每个客户的第一个购买日期
- javascript - 如何列出 Firestore 文档的所有子集合?
- r - 计算数据框中两列之间的偏移量,但忽略其中一列中的一些异常值
- applescript - AppleScript - 如何获取文件夹的第一个子路径(仅当第一个子是文件夹而不是文件时)?