Pergunta

Atualmente estou tentando lidar com a validação exclusiva para entidades como parte do meu método ValidateEntity no meu DbContext.O problema que estou tentando resolver é adicionar várias entidades ao mesmo tempo, detectando erros de restrição exclusivos antes que eles atinjam o banco de dados.Um exemplo é o caso de adicionar as entidades A e B certificando-se de que A e B não tenham o mesmo nome.Atualmente tenho os índices exclusivos aplicados para que pelo menos o banco de dados possa lidar com isso, e tenho o seguinte código que pode cobrir quando A já estiver no banco de dados:

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."));
}

Existe algo mais simples do que fazer o seguinte?

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."));
}

Editar

O caso em que a “chave única” é composta por uma chave estrangeira é uma situação mais complexa.Ao acessar o banco de dados, você precisa usar o campo de chave estrangeira, embora ao acessar o cache local nem sempre seja possível dizer quando ReferenceId == ReferenceId devido a ambos serem zero se as entidades de referência também tiverem sido adicionadas.A maneira correta de verificar o cache local seria a seguinte ou eu precisaria carregar antecipadamente a referência, já que durante a validação o carregamento lento está desativado?

this.Components.Local.Any(x => x != item && x.Name == item.Name && x.ReferenceId == item.ReferenceId && x.Reference == item.Reference)
Foi útil?

Solução

Para resolver meu problema e limitar a reutilização, adicionei as seguintes extensões para ajudar na validação exclusiva.

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

A segunda função trata do caso da chave única ser composta por referências de chave estrangeira.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top