目前,我正在尝试处理实体的唯一验证,作为我的DbContext上的ValidateEntity方法的一部分。我试图解决的问题是在添加多个实体的同时在它到达数据库之前检测到唯一约束错误。一个例子是添加实体A和B时确保A和B不具有相同名称的情况。目前我已经应用了唯一的索引,因此至少数据库将处理它,并且我有以下代码可以涵盖A何时已经在数据库中:
if (this.Components.Any(x => x.Id != item.Id && x.Name == item.Name))
{
result.ValidationErrors.Add(new DbValidationError("Name", "There already exists another component with that name."));
}
有什么比做以下事情更简单吗?
Expression<Func<Component, bool>> predicate = x => x.Name == item.Name;
if (this.Components.Where(x => x.Id != item.Id).Any(predicate) || this.Components.Local.Where(x => x != item).Any(predicate.Compile()))
{
result.ValidationErrors.Add(new DbValidationError("Name", "There already exists another component with that name."));
}
修改
“唯一密钥”由外键组成的情况是更复杂的情况。当针对数据库时,您需要使用外键字段,但是当针对本地缓存时,您不能总是说ReferenceId == ReferenceId
何时由于它们都是零,如果参考实体也刚刚添加。检查本地缓存的正确方法是以下还是我需要急切加载引用,因为在验证延迟加载时关闭了?
this.Components.Local.Any(x => x != item && x.Name == item.Name && x.ReferenceId == item.ReferenceId && x.Reference == item.Reference)
答案 0 :(得分:0)
为了解决我的问题并限制重用,我添加了以下扩展来帮助进行独特的验证。
public static bool UniqueCheck<TSource>(this DbSet<TSource> set, TSource item, Expression<Func<TSource, bool>> uniquePredicate) where TSource : class, IAuditEntity
{
var function = uniquePredicate.Compile();
var localContains = set.Local.Where(x => x != item).Any(function);
if (localContains) return localContains;
var remoteContains = set.Where(x => x.Id != item.Id).Any(uniquePredicate);
return remoteContains;
}
public static bool UniqueCheckWithReference<TSource>(this DbSet<TSource> set, TSource item, Expression<Func<TSource, bool>> uniquePredicate, Expression<Func<TSource, bool>> referenceUniquePredicate) where TSource : class, IAuditEntity
{
var localContains = set.Local.Where(x => x != item).Where(uniquePredicate.Compile()).Where(referenceUniquePredicate.Compile()).Any();
if (localContains) return localContains;
var remoteContains = set.Where(x => x.Id != item.Id).Where(uniquePredicate);
return false;
}
第二个函数处理由外键引用组成的唯一键的情况。