Last active
July 17, 2021 20:21
-
-
Save Lippur/37a4704086c6ccedbc144934931646fb to your computer and use it in GitHub Desktop.
UniqueValidator - Checks if a value is unique in the database for the given entity property
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq.Expressions; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using FluentValidation; | |
using FluentValidation.Validators; | |
using Microsoft.EntityFrameworkCore; | |
using Microsoft.EntityFrameworkCore.Infrastructure; | |
using Microsoft.Extensions.DependencyInjection; | |
namespace Reflex.Core.Database.Rules | |
{ | |
public static class UniqueRule | |
{ | |
/// <summary> | |
/// Check if the given value is unique in the database for the given entity property | |
/// </summary> | |
/// <param name="ruleBuilder"></param> | |
/// <param name="entityExpression">Member access expression for the entity property (e.g. (User user) => user.Email)</param> | |
/// <param name="valueTransformation">Optional delegate applied to the value under validation before comparison</param> | |
/// <typeparam name="T">Type of the object under validation</typeparam> | |
/// <typeparam name="TProperty">Type of the property to be validated</typeparam> | |
/// <typeparam name="TEntity">Type of the entity to check against</typeparam> | |
/// <returns></returns> | |
public static IRuleBuilderOptions<T, TProperty> Unique<T, TProperty, TEntity> ( | |
this IRuleBuilder<T, TProperty> ruleBuilder, | |
Expression<Func<TEntity, TProperty>> entityExpression, | |
Func<TProperty, TProperty>? valueTransformation = null | |
) | |
where TEntity : class | |
{ | |
return ruleBuilder.SetAsyncValidator( | |
new UniqueValidator<T, TProperty, TEntity>(entityExpression, valueTransformation) | |
) | |
.WithMessage("Must be unique"); | |
} | |
} | |
public class UniqueValidator<T, TProperty, TEntity> : AsyncPropertyValidator<T, TProperty> | |
where TEntity : class | |
{ | |
private readonly Expression<Func<TEntity, TProperty>> _property; | |
private readonly Func<TProperty, TProperty>? _valueTransformation; | |
public UniqueValidator ( | |
Expression<Func<TEntity, TProperty>> property, | |
Func<TProperty, TProperty>? valueTransformation = null | |
) | |
{ | |
_property = property; | |
_valueTransformation = valueTransformation; | |
} | |
public override async Task<bool> IsValidAsync ( | |
ValidationContext<T> context, | |
TProperty value, | |
CancellationToken cancellation | |
) | |
{ | |
var db = context.GetServiceProvider() | |
.CreateScope() | |
.ServiceProvider.GetRequiredService<AppDb>() | |
.Set<TEntity>(); | |
var transformedValue = _valueTransformation is null ? value : _valueTransformation(value); | |
var valueExpression = Expression.Constant(transformedValue); | |
var parameter = Expression.Parameter(typeof(TEntity)); | |
var propertyExpression = Expression.Property(parameter, _property.GetPropertyAccess()); | |
var equalityExpression = | |
Expression.Lambda<Func<TEntity, bool>>( | |
Expression.Equal(propertyExpression, valueExpression), | |
parameter | |
); | |
return !await db.AnyAsync(equalityExpression, cancellation); | |
} | |
public override string Name => "UniqueValidator"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment