实体框架正在参数化我的查询以利用缓存

时间:2018-06-15 02:56:32

标签: c# asp.net-core entity-framework-core

我在.Net Core 2.1中工作,创建了一个使用多租户的应用程序。我正在将默认过滤器应用于我的上下文。但是,实体框架未正确利用参数化查询。

我有一个配置选项传递给我的上下文以应用看起来像这样的约束:

select
    app1.teamname as devteam,
    app2.teamname as qateam,
    app3.teamname as prodteam
from
   server
   inner join app as app1 on app1.id = server.dev
   inner join app as app2 on app2.id = server.qa
   inner join app as app3 on app3.id = server.prod
where
   server_name = 'trans';

如您所见,我的查询使用属性来存储userId值。我的上下文接受约束选项广告应用它们OnModels创建如下:

public class ContextAuthorizationOptions : DbAuthorizationOptions<AstootContext>
{
    protected IUserAuthenticationManager _userManager;
    protected int _userId => this._userManager.GetUserId();

    public ContextAuthorizationOptions(IUserAuthenticationManager authenticationManager, IValidatorProvider validatorProvider) 
        : base(validatorProvider)
    {
        this._userManager = authenticationManager;

        ConstraintOptions.SetConstraint<Message>(x => x.Conversation.ConversationSubscriptions
                                         .Select(cs => cs.UserId)
                                         .Any(userId => userId == this._userId));
    }        
}

我的模型选项如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var constraintOptions = this._authorizationOptions.ConstraintOptions;
    constraintOptions.ApplyStaticConstraint(modelBuilder);
    base.OnModelCreating(modelBuilder);
}

由于我的过滤器正在使用属性,我希望这会生成一个参数化查询,但在转储到消息表时,它会生成这个SQL

protected List<Action<ModelBuilder>> _constraints = new List<Action<ModelBuilder>>();

public void SetConstraint<T>(Expression<Func<T, bool>> constraint)
    where T: class
{
    this._constraints.Add(m => m.Entity<T>().HasQueryFilter(constraint));
}

public void ApplyStaticConstraint(ModelBuilder modelBuilder)
{
    foreach(var applyConstraint in this._constraints)
    {
        applyConstraint(modelBuilder);
    }
}

如何修改我的实现,以便Entity Framework Core可以利用查询缓存?

1 个答案:

答案 0 :(得分:2)

由于某些原因,只有EF Core设计人员可以解释,查询过滤器表达式的处理方式与其他查询表达式不同。特别是,所有未 rooted 到目标db上下文的变量都将被计算并转换为常量。 Rooted 术语已经从上下文的简单直接字段/属性发展为#10301: Query: QueryFilter with EntityTypeConfiguration are failing to inject current context values 设计会议记录中解释的更宽松的规则:

  

正确捕获上下文并注入当前实例值的配置模式

     
      
  • OnModelCreating
  • 中定义过滤器   
  • 通过构造函数
  • 传递上下文,在EntityTypeConfiguration中定义过滤器   
  • 使用方法(内部/外部DbContext或扩展方法)定义过滤器,其中上下文作为参数传递。   上面的任何一个上下文被包装在另一个对象类型中并且该类型被传递。
  •   
     

除此之外,我们将参数化DbContext上的任何类型的呼叫,即属性/字段访问,方法调用,通过多个级别。

子弹#3(&#34;使用方法定义过滤器(内部/外部DbContext或扩展方法),其中上下文作为参数传递。&#34; )引导我一个相对简单的通用解决方案。

添加以下简单类:

public static class Filter
{
    public static T Variable<T>(this DbContext context, T value) => value;
}

像这样修改你的选项类:

protected List<Action<ModelBuilder, DbContext>> _constraints = new List<Action<ModelBuilder, DbContext>>();

public void SetConstraint<T>(Func<DbContext, Expression<Func<T, bool>>> constraint)
    where T : class
{
    this._constraints.Add((mb, c) => mb.Entity<T>().HasQueryFilter(constraint(c)));
}

public void ApplyStaticConstraint(ModelBuilder modelBuilder, DbContext context)
{
    foreach (var applyConstraint in this._constraints)
    {
        applyConstraint(modelBuilder, context);
    }
}

SetConstraint这样的调用(请注意将this._userId包装到Variable方法调用中):

ConstraintOptions.SetConstraint<Message>(c => x => x.Conversation.ConversationSubscriptions
    .Select(cs => cs.UserId)
    .Any(userId => userId == c.Variable(this._userId)));

最后是ApplyStaticConstraint来电:

constraintOptions.ApplyStaticConstraint(modelBuilder, this);

现在查询将使用参数而不是常量值。