本地和数据库上的实体框架唯一验证



目前,我正在尝试处理实体的唯一验证,作为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)

为了解决我的问题并限制重用,我添加了以下扩展以帮助进行唯一验证。

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

第二个函数处理唯一密钥由外键引用组成的情况。

最新更新