我在.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可以利用查询缓存?
答案 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);
现在查询将使用参数而不是常量值。