首页 > 解决方案 > 编写一个在 DbContext 重新评估之间重新评估的 LambdaExpression?

问题描述

我正在尝试使用EF Core 3.0 中的全局查询过滤器。总的来说,他们工作得相当好,但有一个小问题。我一直在尝试以类似于此处指南的方式设置多租户。我的用例有点不同,因为租户是从查询字符串参数(或标头)派生的,并且在请求之间会发生变化。

如果我IOrganizationEntity用一个名为Organization. 我可以使用语句定义全局查询过滤器entity.HasQueryFilter<IOrganizationEntity>(o => o.Organization == OrganizationId),这可以按预期工作。但是,我试图避免Organization在我的实体 POCO 上放置一个属性,而是尝试使用类似于:

private Guid OrganizationId => HasOrganization ? _organizationService.GetCurrentOrganizationId() : Guid.Empty;

private void ConfigureOrganizationFilter(ModelBuilder modelBuilder)
{
    foreach (var entity in modelBuilder.Model.GetEntityTypes().Where(x =>
        typeof(IOrganizationEntity).IsAssignableFrom(x.ClrType)))
    {
        entity.AddProperty(nameof(Organization), typeof(Guid));
        // Note, AddQueryFilter is an extension method that allows us to add multiple
        // query filters to a single type of entity.
        entity.AddQueryFilter(IsOrganizationRestricted(entity.ClrType));
    }
}

private LambdaExpression IsOrganizationRestricted(Type type)
{
    var propertyMethod = typeof(EF).GetMethod(nameof(EF.Property),
        BindingFlags.Static |
        BindingFlags.Public)?.MakeGenericMethod(typeof(Guid));

    if (propertyMethod == null)
        throw new ArgumentException("Object does not appear to be valid for operation.");

    var param = Expression.Parameter(type, "it");
    var prop = Expression.Call(propertyMethod, param, Expression.Constant(nameof(Organization)));
    var condition = Expression.MakeBinary(ExpressionType.Equal, prop, Expression.Constant(OrganizationId));
    var lambda = Expression.Lambda(condition, param);

    return lambda;
}

但是,当我使用上面的代码时,过滤在第一次运行时会按预期工作,但在后续运行OrganizationId发生变化时会失败。我对Expression明确编写函数还很陌生,所以我不确定需要发生什么来强制重新评估 OrganizationId

标签: c#entity-frameworklambdaentity-framework-core

解决方案


为了重新评估,过滤器表达式必须访问 db 上下文派生类的属性/字段/方法。

假设有问题的代码位于您的数据库上下文派生类中,即OrganizationId是该类的属性,问题是

Expression.Constant(OrganizationId)

在您构建过滤器时对其进行评估,并将一个量值绑定到过滤器表达式中。

相反,您应该像这样绑定OrganizationId属性访问器表达式(即Expression.Property):

// this.OrganizationId
Expression.Property(Expression.Constant(this), nameof(OrganizationId)) 

推荐阅读