【问题标题】:How can I rewrite this LINQ query with reflection如何使用反射重写此 LINQ 查询
【发布时间】:2015-08-27 23:03:02
【问题描述】:

所以我使用反射编写了这个 LINQ 查询,后来发现它不受支持。从这段代码中获得相同功能的最佳方法是什么?

List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>().Where(p => typeof(Profile)
.GetProperty(handler.Name + "UUID").GetValue(p) == obj.uuid).ToListAsync();

【问题讨论】:

    标签: c# linq dynamic reflection


    【解决方案1】:

    使用反射来创建查询,而不是在查询中。考虑:

    public static IQueryable<Profile> Filter(
      this IQueryable<Profile> source, string name, Guid uuid)
    {
      // .<name>UUID
      var property = typeof(Profile).GetProperty(name + "UUID");
    
      // p
      var parExp = Expression.Parameter(typeof(Profile));
    
      // p.<name>UUID
      var methodExp = Expression.Property(parExp, property);     
    
      // uuid
      var constExp = Expression.Constant(uuid, typeof(Guid));    
    
      // p.<name>UUID == uuid
      var binExp = Expression.Equal(methodExp, constExp);                  
    
      // p => p.<name>UUID == uuid
      var lambda = Expression.Lambda<Func<Profile, bool>>(binExp, parExp);
    
      // source.Where(p => p.<name>UUID == uuid)
      return source.Where(lambda);
    }
    

    这首先构建表达式(因此,如果 name 是“测试”,它将创建与 p =&gt; p.TestUUID == uuid 对应的表达式,然后在对 Where 的调用中使用它。

    因为这一步是首先完成的,而不是在表达式本身中完成,所以查询引擎不需要尝试将typeofGetProperty() 转换为 SQL(当然,它不能这样做)。

    所以:

    var filtered = MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid);
    

    返回一个IQueryable&lt;Profile&gt;,并附上相应的Where。所以:

    var profilesFromUUID = await MobileService.GetTable<Profile>().Filter(handler.Name, obj.uuid).ToListAsync();
    

    作为一个整体,首先使用反射来构建查询,然后应用查询,然后异步生成一个列表,然后等待其结果。

    值得注意的是,由于Filter() 将接受任何IQueryable&lt;Profile&gt;,因此它们可以被链接或联合。所以:

    MobileService.GetTable<Profile>().Filter("A", uuid0).Filter("B", uuid1);
    

    相当于:

    from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 && p.BUUID == uuid1
    

    还有:

    MobileService.GetTable<Profile>().Filter("A", uuid0).Union(
      MobileService.GetTable<Profile>().Filter("B", uuid1))
    

    相当于:

    from p in MobileService.GetTable<Profile>() where p.AUUID = uuid0 || p.BUUID == uuid1
    

    更通用的版本是:

    public static IQueryable<TSource> FilterByNamedProperty<TSource, TValue>(this IQueryable<TSource> source, string propertyName, TValue value)
    {
      var property = typeof(TSource).GetProperty(propertyName);
      var parExp = Expression.Parameter(typeof(TSource));
      var methodExp = Expression.Property(parExp, property);
      var constExp = Expression.Constant(value, typeof(TValue));
      var binExp = Expression.Equal(methodExp, constExp);
      var lambda = Expression.Lambda<Func<TSource, bool>>(binExp, parExp);
      return source.Where(lambda);
    }
    

    然后,虽然您必须在调用代码中执行 + "UUID",但您可以使用它对任何元素类型的任何 IQueryable&lt;&gt; 进行类似查询。

    【讨论】:

    • 我将如何使用var profilesFromUUID = await MobileService.GetTable&lt;Profile&gt;().Filter(handler.Name, obj.uuid).ToListAsync(); 因为Filter 不是派生的方法MobileService.GetTable&lt;&gt;()
    • 哦,我猜它需要像扩展一样使用?
    • 是的,因此方法签名中的this。使它成为一个扩展使它类似于Where,事实上,这让我想到了一个更通用的版本。
    【解决方案2】:

    只比较所有属性名称怎么样?根据定义,UUID 不会无论如何都不会发生冲突。由于Profile 只是一个数据类,所以UUID 的属性# 是固定的。

    List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
        .Where(p => 
            p.A_UUID == obj.uuid || 
            p.B_UUID == obj.uuid ||
            p.C_UUID == obj.uuid)
        .ToListAsync();
    

    或者为 Profile 添加一个方法(扩展方法),比如:

    public static Guid GetUUIDByTableName(this Profile value, string tableName)
    {
        switch (tableName)
        {
            case "A_": return value.A_UUID;
            case "B_": return value.B_UUID;
            default: return Guid.Empty;
        }
    }
    
    
    List<Profile> profilesFromUUID = await MobileService.GetTable<Profile>()
        .Where(p => p.GetUUIDByTableName(handler.Name) == obj.uuid)
        .ToListAsync();
    

    【讨论】:

    • 嗯,这当然是一种方法。然而问题是 Profile 表有很多其他表的外键,它们都有自己的 uuid。所以我需要找到与ForeignKeyTableName + UUID 字符串匹配的特定uuid。我认为会有更好的方法来做到这一点,因为我知道我正在寻找的 uuid 的字符串值
    • @DillonDrobena 我不认为添加条件(handler.Name == "A_" &amp;&amp; p.A_UUID == obj.uuid) 对性能有多大帮助。UID 的比较应该足够好。
    • 我的意思是 handler.Name 包含我需要立即找到该属性的信息。所以,例如(使用反射)handler.Name = TableA 所以通过使用反射我可以立即获得我正在寻找的属性,而不必查看obj.uuid == TableA.uuid || obj.uuid == TableB.uuid || ... || TableZ.uuid = obj.uuid 如果我找不到方法,我会使用你的建议在查询中使用反射
    • 您需要添加代码以允许GetUUIDByTableName 作为 SQL 执行,否则它将无法与查询提供程序一起使用,就像问题中的原始代码一样。
    • 如果您使用IQueryable&lt;Profile&gt;,在切换中应用Where,然后返回生成的IQueryable&lt;Profile&gt;,则切换方法可以与数据库一起使用。
    猜你喜欢
    • 1970-01-01
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    • 2013-04-30
    • 1970-01-01
    • 1970-01-01
    • 2021-01-15
    • 1970-01-01
    相关资源
    最近更新 更多