实体框架授权IDbCommandTreeInterceptor

时间:2016-12-16 08:27:06

标签: c# entity-framework linq entity-framework-6 linq-to-entities

我正在尝试实现向所有Entity Framework查询添加授权SQL过滤器的一般方法。 为此,我使用IDbCommandTreeInterceptor和DefaultExpressionVisitor。 模型是基本的:我们所有的实体都有一个RegionID,我们在Db上下文中有一个DynRegions实体集,其中包含用户可以访问的所有区域。

所以我想在下面的linq查询中动态添加Any过滤器(产生SQL SELECT EXISTS):

var query = context.Contacts
 .Where(
    c =>
        context.DynRegions.Any(r => r.UserId == "150293818FC844F89AB36D044988F146" && r.RegionId == c.RegionId) &&
        c.LastName.StartsWith("A"));

我已经开始使用此代码了:

public class RegionsInterceptor : IDbCommandTreeInterceptor
{
    public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
    {
        if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace) return;

        var queryCommand = interceptionContext.Result as DbQueryCommandTree;
        if (queryCommand == null) return;

        var dbContext = interceptionContext.DbContexts.FirstOrDefault() as CrmContext;
        if (dbContext == null) return;

        var newQuery = queryCommand.Query.Accept(new RegionsQueryVisitor(dbContext));
        interceptionContext.Result = new DbQueryCommandTree(
                                            queryCommand.MetadataWorkspace,
                                            queryCommand.DataSpace,
                                            newQuery);
    }
}

public class RegionsQueryVisitor : DefaultExpressionVisitor
{
    private readonly CrmContext dbContext;

    public RegionsQueryVisitor(CrmContext dbContext)
    {
        this.dbContext = dbContext;
    }

    public override DbExpression Visit(DbScanExpression expression)
    {
        var dbExpression = base.Visit(expression);

        var currentExpressionBinding = DbExpressionBuilder.Bind(dbExpression);
        var currentEntityRegionId = DbExpressionBuilder.Property(currentExpressionBinding.Variable, "RegionId");

        var rolesExpression = expression.Target.EntityContainer.GetEntitySetByName("DynRole", true).Scan();
        var rolesExpressionBinding = rolesExpression.Bind();

        var rolesRegionIdProperty = DbExpressionBuilder.Property(rolesExpressionBinding.Variable, "RegionId");
        var rolesUserIdProperty = DbExpressionBuilder.Property(rolesExpressionBinding.Variable, "UserId");
        var userIdParameter = DbExpressionBuilder.Constant("150293818FC844F89AB36D044988F146");

        var anyRolesExpression =
            rolesExpression.Any(p => 
                    DbExpressionBuilder.Equal(rolesUserIdProperty, userIdParameter)
                        .And(
                    DbExpressionBuilder.Equal(rolesRegionIdProperty, currentEntityRegionId)));


        var binding = expression.Bind();

        return binding.Filter(anyRolesExpression);
    }    
}

我收到错误:引用的变量'Var_2'未在当前范围中定义。 错误发生在以下行:

interceptionContext.Result = new DbQueryCommandTree(
                                             queryCommand.MetadataWorkspace,
                                             queryCommand.DataSpace,
                                             newQuery);

有什么想法吗?有没有人写过这样的拦截器/访客? 任何帮助/指针将不胜感激。

谢谢, 亚历

0 个答案:

没有答案