按键表达式存储静态过滤器

2023-12-12

我有一个函数,当传入一个时,它会生成一个表达式来通过主键过滤表Object[],这非常类似于Find函数,只是它没有实现,所以你可以传递一个IQueryable之后周围

public static Expression<Func<T, bool>> FilterByPrimaryKeyPredicate<T>(this DbContext dbContext, object[] id)
{
    var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
    var parameter = Expression.Parameter(typeof(T), "e");
    var body = keyProperties
        // e => e.{propertyName} == new {id = id[i]}.id
        .Select((p, i) => Expression.Equal(
            Expression.Property(parameter, p.Name),
            Expression.Convert(
                Expression.PropertyOrField(Expression.Constant(new { id = id[i] }), "id"),
                p.ClrType)))
        .Aggregate(Expression.AndAlso);
    return Expression.Lambda<Func<T, bool>>(body, parameter);
}

它的工作原理是首先获取表的主键,它为每个属性创建二进制表达式,ID 包装在匿名类型中以利用查询缓存。这工作正常。然而,我想更进一步。

我想保留表达式,这样我就不必每次传递一组新的 id 时都生成它,我该如何存储它Expression同时仍然利用查询缓存?

编辑 TL;DR

因此,我尝试按照建议在静态类中使用数组访问来缓存它,但是我遇到了错误:

public class PrimaryKeyFilterContainer<T>
{
    const string ANON_ID_PROP = "id";
    static Expression<Func<T, bool>> _filter;
    Type ANON_TYPE = new { id = (object)0 }.GetType();
    public object[] id { get; set; }

    public PrimaryKeyFilterContainer()
    {
    }

    public Expression<Func<T, bool>> GetFilter(DbContext dbContext, object[] id)
    {
        this.id = id;

        if(null == _filter)
        {
            var keyProperties = dbContext.GetPrimaryKeyProperties<T>();
            var parameter = Expression.Parameter(typeof(T), "e");
            var body = keyProperties
                // e => e.PK[i] == id[i]
                .Select((p, i) => Expression.Equal(
                    Expression.Property(parameter, p.Name),
                    Expression.Convert(BuildNewExpression(i),
                        p.ClrType)))
                .Aggregate(Expression.AndAlso);

            _filter = Expression.Lambda<Func<T, bool>>(body, parameter);
        }

        return _filter;
    }

    NewExpression BuildNewExpression(int index)
    {
        var currentObject = Expression.Constant(this);
        var fieldAccess = Expression.PropertyOrField(currentObject, nameof(id));
        var arrayAccess = Expression.ArrayAccess(fieldAccess, Expression.Constant(index));
        return Expression.New(ANON_TYPE.GetConstructor(new[] { typeof(object) }), arrayAccess);
    }
}

类型 'f__AnonymousType0`1[System.Object]' 和 'System.Int32' 之间未定义强制运算符

我越来越接近了,但我不确定它是否仍然有效。


正如我在评论中提到的,主要问题是我们无法在表达式树内使用数组索引访问 - EF6 抛出不支持的异常,EF Core 将其转换为客户端评估。

因此,我们需要将键存储在具有动态属性和属性类型计数的类中。幸运的是System.Tuple泛型类提供此类功能,并且可以在 EF6 和 EF Core 中使用。

下面是一个实现上述想法的类:

public class PrimaryKeyFilter<TEntity>
    where TEntity : class
{
    object valueBuffer;
    Func<object[], object> valueArrayConverter;

    public PrimaryKeyFilter(DbContext dbContext)
    {
        var keyProperties = dbContext.GetPrimaryKeyProperties<TEntity>();

        // Create value buffer type (Tuple) from key properties
        var valueBufferType = TupleTypes[keyProperties.Count - 1]
            .MakeGenericType(keyProperties.Select(p => p.ClrType).ToArray());

        // Build the delegate for converting value array to value buffer
        {
            // object[] values => new Tuple(values[0], values[1], ...)
            var parameter = Expression.Parameter(typeof(object[]), "values");
            var body = Expression.New(
                valueBufferType.GetConstructors().Single(),
                keyProperties.Select((p, i) => Expression.Convert(
                    Expression.ArrayIndex(parameter, Expression.Constant(i)),
                    p.ClrType)));
            valueArrayConverter = Expression.Lambda<Func<object[], object>>(body, parameter).Compile();
        }

        // Build the predicate expression
        {
            var parameter = Expression.Parameter(typeof(TEntity), "e");
            var valueBuffer = Expression.Convert(
                Expression.Field(Expression.Constant(this), nameof(this.valueBuffer)),
                valueBufferType);
            var body = keyProperties
                // e => e.{propertyName} == valueBuffer.Item{i + 1}
                .Select((p, i) => Expression.Equal(
                    Expression.Property(parameter, p.Name),
                    Expression.Property(valueBuffer, $"Item{i + 1}")))
                .Aggregate(Expression.AndAlso);
            Predicate = Expression.Lambda<Func<TEntity, bool>>(body, parameter);
        }
    }

    public Expression<Func<TEntity, bool>> Predicate { get; }

    public void SetValues(params object[] values) =>
        valueBuffer = valueArrayConverter(values);

    static readonly Type[] TupleTypes =
    {
        typeof(Tuple<>),
        typeof(Tuple<,>),
        typeof(Tuple<,,>),
        typeof(Tuple<,,,>),
        typeof(Tuple<,,,,>),
        typeof(Tuple<,,,,,>),
        typeof(Tuple<,,,,,,>),
        typeof(Tuple<,,,,,,,>),
    };
}

您可以创建并存储该类的实例。然后使用返回的表达式Predicate查询中的属性。和SetValues方法来设置参数。

缺点是值存储与类实例绑定,因此不能同时使用。原始方法在所有情况下都适用,并且对性能的影响 IMO 应该可以忽略不计,因此您可能会考虑继续使用它。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

按键表达式存储静态过滤器 的相关文章

  • SetWindowsHookEx 函数返回 NULL

    我正在研究 DLL 注入 但收到错误如下 挂接进程失败 87 参数不正确 目标进程和dll都是64位的 注入代码为 BOOL HookInjection TCHAR target TCHAR dll name https msdn micr
  • C修改printf()输出到文件

    有没有办法修改printf为了将字符串输出到文件而不是控制台 我尝试在互联网上查找一些内容 发现了类似的电话dup dup2 and fflush这可能与此有关 EDIT 也许我不清楚 问题是这是C考试问题 问题如下 解释一个通常将字符串输
  • 检查列表是否包含另一个列表。 C#

    编辑 只是说 ContainsAllItem 中的注释解释得最好 很抱歉问这个问题 我知道以前有人问过这个问题 但我只是不明白 好的 所以我想检查一个列表是否包含另一个列表中的所有项目WITHOUT重叠 以及根据类字符串 名称变量 称为项目
  • 切换图片框可见性 C#

    为什么图片框控件的可见性属性在这里不起作用 我最初将它们设置为 false 以便在屏幕加载时它们不可见 但后来我想切换这个 我已完成以下操作 但似乎不起作用 这是一个 Windows 窗体应用程序 private void Action w
  • VS 2010 Web应用程序中的ASP.NET Web Api CRUD操作

    我尝试在 VS 2010 Web 应用程序中进行 ASP NET Web Api CRUD 操作 但为什么结果没有从源表返回所有整行 这是我的代码 路线 Globax asax protected void Application Star
  • Android NDK C++“wstring”支持

    我有用 C 编写的源代码 lib 现在我想在 Android NDK 项目 NDK 6 中编译并使用相同的源代码 lib 我能够编译大多数 C 文件 除了基于 std wstring 的功能 在 Application mk 中 当我指定时
  • 将成员函数作为参数传递/c++

    我想用 C 实现一个类b可以通过封装该迭代器类型的成员集进行某种迭代 喜欢 b object for each x do function f so 函数 f会得到每个人的x成员并做任何事情 比方说 void function f x me
  • 使用 openssl 检查服务器安全协议

    我有一个框架应用程序 它根据使用方式连接到不同的服务器 对于 https 连接 使用 openssl 我的问题是 我需要知道我连接的服务器是否使用 SSL 还是 TLS 以便我可以创建正确的 SSL 上下文 目前 如果我使用错误的上下文尝试
  • 导出到 CSV 时 Gridview 出现空行

    这个问题是由进一步讨论引发的这个问题 https stackoverflow com questions 6674555 export gridview data into csv file 6674589 noredirect 1 com
  • 如何使用泛型类型的 DataContractSerializer 编写自定义序列化器?

    我想编写一个自定义序列化器 用于将会话状态存储到Azure 缓存 预览版 这意味着这个自定义序列化器必须实现IDataCacheObjectSerializer 如果我错了 请告诉我 我需要编写这个自定义序列化程序的原因是我需要序列化一些包
  • QThread - 使用槽 quit() 退出线程

    我想在线程完成运行时通知对象 但是 我无法让线程正确退出 我有以下代码 处理器 cpp thread new QThread tw new ThreadWorker connect tw SIGNAL updateStatus QStrin
  • 为什么重载方法在 ref 仅符合 CLS 方面有所不同

    公共语言规范对方法重载非常严格 仅允许根据其参数的数量和类型来重载方法 如果是泛型方法 则根据其泛型参数的数量进行重载 根据 csc 为什么此代码符合 CLS 无 CS3006 警告 using System assembly CLSCom
  • 不要声明只读可变引用类型 - 为什么不呢?

    我一直在阅读这个问题 https stackoverflow com questions 2274412 immutable readonly reference types fxcop violation do not declare r
  • 有没有更好的方法来获取每个项目与谓词匹配的子序列?

    假设我有一个 IEnumerable 例如 2 1 42 0 9 6 5 3 8 我需要获得与谓词匹配的项目的 运行 例如 如果我的谓词是 bool isSmallerThanSix int number 我想得到以下输出 2 1 0 5
  • 你能解释一下这个C++删除问题吗?

    我有以下代码 std string F WideString ws GetMyWideString std string ret StringUtils ConvertWideStringToUTF8 ws ret return ret W
  • 为什么 C# 接口名称前面加上“I”

    这种命名约定背后的基本原理是什么 我没有看到任何好处 额外的前缀只会污染 API 我的想法与康拉德一致response https stackoverflow com a 222502 9898与此相关的question https sta
  • c# 替代方案中 cfusion_encrypt 中填充的密钥是什么?

    我找到了从这里复制 C 中的 cfusion encrypt 函数的答案 ColdFusion cfusion encrypt 和 cfusion decrypt C 替代方案 https stackoverflow com questio
  • 在何处将 CFLAG(例如 -std=gnu99)添加到 (Eclipse CDT) 自动工具项目中

    我有一个简单的 Autotools C 项目 不是 C 其框架是由 Eclipse CDT Juno 为我创建的 CFLAG 通过检查 似乎是 g O2 我希望所有生成的 make 文件也具有 std gnu99附加到 CFLAG 因为我使
  • 使用剪贴板 SetText 换行

    如何使用 SetText 方法添加换行符 I tried Clipboard SetText eee n xxxx 但当我将剪贴板数据粘贴到记事本中时 它没有给我预期的结果 预期结果 eee xxxx 我怎样才能做到这一点 Windows
  • 如何从函数返回矩阵(二维数组)? (C)

    我创建了一个生成宾果板的函数 我想返回宾果板 正如我没想到的那样 它不起作用 这是函数 int generateBoard int board N M i j fillNum Boolean exists True initilize se

随机推荐