【问题标题】:.Net 5 Entity Framework Generic Any Query.Net 5 实体框架通用任何查询
【发布时间】:2021-01-04 18:54:23
【问题描述】:

由于我是一只懒狗,我想为 .net FluentValidation 实现一个通用的 UniqueValidator。目标很简单:有一个验证器,我将模型传递给该表达式,以获取必须唯一的属性/字段并运行 EF Any 查询。这样可以避免每次必须验证 DB 中值的唯一性时都编写一个愚蠢的类。

在对我看来是一个公平的解决方案进行一些调整后,我想出了一个避免将预编译的 Lambda 传递和调用到 EF 查询转换器的公平解决方案,这当然会导致 expression could not be translated 异常。

这是我已经实现的:

public class UniqueValidator : IUniqueValidator
{
    private ApplicationContext _dbContext;

    public UniqueValidator(ApplicationContext dbContext)
    {
        _dbContext = dbContext;
    }

    public async Task<bool> IsUnique<T>(T model, Expression<Func<T, string>> expression, CancellationToken cancellationToken) where T : class
    {
        // Getting the value to check for unicity:
        Func<T, string> method = expression.Compile(true);
        var value = method(model);

        // For GetDbSet<T>() test purpose, run perfectly:
        bool test = await GetDbSet<T>().OfType<BL.Driver>().AnyAsync(d => d.Email == "any.valid@email.com");


        // Building Linq expression
        var binaryExpression = Expression.Equal(expression.Body, Expression.Constant(value));
        var pe = new ParameterExpression[] { Expression.Parameter(typeof(T)) };

        var anyExpression = Expression.Lambda<Func<T, bool>>(binaryExpression, pe);

        return !(await GetDbSet<T>().AnyAsync(anyExpression));
    }

    private DbSet<T> GetDbSet<T>() where T : class
    {
        return (DbSet<T>)typeof(ApplicationContext)
            .GetProperties()
            .FirstOrDefault(p => p.PropertyType == typeof(DbSet<T>))
            .GetValue(_dbContext);
    }
}

验证器的使用方法如下:

RuleFor(d => d)
    .MustAsync((driver, cancellationToken) => {
        return uv.IsUnique(driver, d => d.Email, cancellationToken);
    });

不幸的是,这引发了一个非常麻烦且无用的异常:

System.InvalidOperationException: The LINQ expression 'DbSet<Driver>() .Any(d => d.Email == "any.valid@email.com")' could not be translated...

注意:在 UniqueValidator 实现中,我添加了一行来测试异常中描述的这个非常相同的查询,它运行完美,只是为了消除对查询有效性的任何疑问。

我想问题出在expression.Body 的翻译上,但看不到任何原因,也看不出如何解决它。 任何帮助将不胜感激。

【问题讨论】:

  • 您的GetDbSet 可以替换为DbContext.Set&lt;T&gt;() docs.microsoft.com/en-us/dotnet/api/…
  • @DavidBrowne-Microsoft 确实有助于提高性能,但这并不能解决问题,无论如何,谢谢。
  • 这就是为什么它是一个评论。我很久以前就不再尝试使用表达式问题来回答动态 LINQ 流了。
  • 是的,我明白了,只为其他读者和后人提及:)

标签: c# entity-framework generic-programming .net-5


【解决方案1】:

您必须重用表达式的原始参数,或者您必须使用表达式替换器:

var pe = new ParameterExpression[] { Expression.Parameter(typeof(T)) };

改成

var pe = expression.Parameters[0];

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多