Mar*_*son 14 c# entity-framework entity-framework-6 entity-framework-6.1
我正在尝试使用Entity Framework CommandTree拦截器通过DbContext为每个查询添加一个过滤器.
为简单起见,我有两个表,一个名为"User",有两列("UserId"和"EmailAddress"),另一个名为"TenantUser",有两列("UserId"和"TenantId").
每次有User表的DbScan时,我想对TenantUser表进行内连接并根据TenantId列进行过滤.
有一个名为EntityFramework.Filters的项目沿着这些方向做了一些事情,但不支持"复杂连接",这似乎是我正在尝试做的事情.
在TechEd 2014的演示之后,我创建了一个拦截器,它使用带有下面方法的访问者用DbJoinExpression替换DbScanExpressions.一旦我开始工作,我计划将其包装在DbFilterExpression中,以将TenantId列与已知ID进行比较.
public override DbExpression Visit(DbScanExpression expression)
{
var table = expression.Target.ElementType as EntityType;
if (table != null && table.Name == "User")
{
return DbExpressionBuilder.InnerJoin(expression, DbExpressionBuilder.Scan(expression.Target), (l, r) =>
DbExpressionBuilder.Equal(DbExpressionBuilder.Variable(tenantUserIdProperty.TypeUsage, "UserId"),
DbExpressionBuilder.Variable(userIdProperty.TypeUsage, "UserId")));
}
return base.Visit(expression);
}
Run Code Online (Sandbox Code Playgroud)
为了测试上面的代码,我已经将拦截器添加到dbContext并运行以下代码:
dbContext.Users.Select(u => new { u.EmailAddress }).ToList();
Run Code Online (Sandbox Code Playgroud)
但是,这会导致以下错误:
没有名称为'EmailAddress'的属性由类型'Transient.rowtype [(l,CodeFirstDatabaseSchema.User(Nullable = True,DefaultValue =)),(r,CodeFirstDatabaseSchema.User(Nullable = True,DefaultValue =))]声明".
我是否错误地构建了DbJoinExpression?还是我错过了别的什么?
您获得该异常的原因是因为 InnerJoin 生成两个表中列的组合结果,另一方面查询应该返回 User 类的那些匹配属性,因此您还需要在查询末尾使用投影。这是对我有用的代码:
public override DbExpression Visit(DbScanExpression expression)
{
var table = expression.Target.ElementType as EntityType;
if (table != null && table.Name == "User")
{
return expression.InnerJoin(
DbExpressionBuilder.Scan(expression.Target.EntityContainer.BaseEntitySets.Single(s => s.Name == "TennantUser")),
(l, r) =>
DbExpressionBuilder.Equal(
DbExpressionBuilder.Property(l, "UserId"),
DbExpressionBuilder.Property(r, "UserId")
)
)
.Select(exp =>
new {
UserId = exp.Property("l").Property("UserId"),
Email = exp.Property("l").Property("Email")
});
}
return base.Visit(expression);
}
Run Code Online (Sandbox Code Playgroud)
正如您在连接操作后看到的,您可以通过使用指定连接条件的表达式中的 lambda 表达式别名来引用特定的连接表。因此,在我的例子中,您将 User 表称为 l,将 TennantUser 称为 r。在发送到数据库的 SQL 查询结果中将使用字母 l 和 r 以及别名。在 InnerJoin 和 Select 操作之间,您可以放置所需的其他逻辑,例如 Filter 等。