【问题标题】:Call function in dynamic linq动态linq中的调用函数
【发布时间】:2013-08-21 05:22:34
【问题描述】:

我正在尝试在动态 linq select 语句中调用函数,但出现错误:

No property or field 'A' exists in type 'Tuple2'

示例代码:

void Main()
{
    var a = new Tuple<int, int>(1,1);
    var b = new[]{ a };
    var q = b.AsQueryable().Select("A.Test(it.Item1)");

    q.Dump();
}

public static class A
{
    public static int Test(int i)
    {
        return i++;
    }
}

我应该如何更改我的代码以使其正常工作?

例如,如果我调用内置函数 Convert.ToInt32,它可以正常工作。

var q = b.AsQueryable().Select("Convert.ToInt32(it.Item1)");

另外,我如何使用动态 linq 转换属性?

var q = b.AsQueryable().Select("((float)it.Item1)");

【问题讨论】:

  • Enumerable.Select 方法中使用字符串的语法是什么?
  • @Bob。写在标签里:dynamic-linq

标签: c# linq linq-to-objects dynamic-linq


【解决方案1】:

我会说dynamic-linq 不够“强大”来做这些事情。它仅在给定对象和一些特殊类中查找方法:MathConvert、各种基本类型(intfloatstring、...)、Guid、@987654329 @,DateTime

如果您在文件上使用 ilspy/reflector,这些类型的列表是清晰可见的。他们在System.Linq.Dynamic.ExpressionParser.predefinedTypes

现在,显然我可能是错的,但这有效:.Select("Guid.NewGuid().ToString()").Cast&lt;string&gt;().ToArray()

表明这很可能是“正确”列表。

这里有一篇关于如何修改Dynamic LINQ的文章,如果你有兴趣http://www.krizzcode.com/2012/01/extending-dynamiclinq-language.html

现在,聪明人会获取动态 linq 的源并简单地扩展该数组...但是这里没有聪明人...只有程序员想要,尤其是内脏

var type = typeof(DynamicQueryable).Assembly.GetType("System.Linq.Dynamic.ExpressionParser");

FieldInfo field = type.GetField("predefinedTypes", BindingFlags.Static | BindingFlags.NonPublic);

Type[] predefinedTypes = (Type[])field.GetValue(null);

Array.Resize(ref predefinedTypes, predefinedTypes.Length + 1);
predefinedTypes[predefinedTypes.Length - 1] = typeof(A); // Your type

field.SetValue(null, predefinedTypes);

在第一次调用 Dynamic Linq 之前执行此操作(使用您想要的类型)(因为在第一次调用之后这些类型的方法/属性被缓存)

说明:我们使用反射将我们的对象添加到这个“特殊列表”中。

【讨论】:

  • @andy 不可能的答案仍然是答案
  • 如果你像我一样,想炸掉缓存,以防你想在任何时候摆弄内脏,你可以在上面代码的末尾标记这个:field = type.GetField("keywords", BindingFlags.Static | BindingFlags.NonPublic); field.SetValue(null, null);
【解决方案2】:

我知道对此已经有一个公认的答案,但它对我不起作用。我正在使用动态 Linq 1.1.4。我想做这样的查询

$.GetNewestRisk() == null

GetNewestRisk() 是 $ 表示的对象上的公共方法。我不断收到此错误“运行查询时出错,'Patient' 类型的方法不可访问(在索引 2 处)”。

我在源代码中发现有一个 GlobalConfig 对象,它允许分配一个自定义提供程序,该提供程序将包含您可能想要使用的所有类型。这是自定义提供程序的源代码:

public class CustomTypeProvider: IDynamicLinkCustomTypeProvider
{
    public HashSet<Type> GetCustomTypes()
    {
        HashSet<Type> types = new HashSet<Type>();
        types.Add(typeof(Patient));
        types.Add(typeof(RiskFactorResult));
        types.Add(typeof(PatientLabResult));
        types.Add(typeof(PatientVital));
        return types;
    }
}

这是我的使用方法:

System.Linq.Dynamic.GlobalConfig.CustomTypeProvider = new CustomType();

进行此调用后,我可以在表达式内的对象上调用方法。

【讨论】:

  • 我能够将此答案改编为 System.Linq.Dynamic.Core。它具有相同的接口,但也具有 ParsingConfig,它允许您为单个表达式提供类型,而不是全局应用自定义类型。
【解决方案3】:

@xanatos 答案不适用于 .Net Core 版本。因此,我在库作者本人编写的 System.Dynamic.Linq.Core 测试 DynamicExpressionParserTests 上发现了与 @Kent 相关的类似内容。

给定的TestCustomTypeProviderClass 允许您使用DynamicLinqType 类注解,这对这个问题非常有用。

要回答问题,您只需定义类(确保使用 DynamicLinqType 进行注释):

[DynamicLinqType] 
public static class A
{
   public static int Test(int i)
   {
      return i++;
   }
}

如上所述添加一个customTypeProvider:

private class TestCustomTypeProvider : AbstractDynamicLinqCustomTypeProvider, IDynamicLinkCustomTypeProvider
{
   private HashSet<Type> _customTypes;

   public virtual HashSet<Type> GetCustomTypes()
   {
      if (_customTypes != null)
      {
          return _customTypes;
      }

      _customTypes = new HashSet<Type>(FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly }));
            return _customTypes;
    }
}

并使用带有the configurable Select 的 ParsingConfig 来调用它:

var config = new ParsingConfig
{
     CustomTypeProvider = new TestCustomTypeProvider()
};

var q = b.AsQueryable().Select(config, "A.Test(it.Item1)");

【讨论】:

  • 如果您将函数定义到另一个程序集中,请查看 Chris Fraher 的附加答案。
  • 这已被包含为默认的自定义解析器。所以如果你的类在同一个程序集中,你只需要装饰它们,不需要额外的配置。
【解决方案4】:

@Armand 为这个问题提供了一个绝妙的解决方案,作为我能找到的唯一解决方案,我想为尝试相同方法的任何人添加它。

标有...的类

[DynamicLinqType] 

... 运行以下行时必须考虑到:

FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { GetType().GetTypeInfo().Assembly })

在上面提供的解决方案中,假设包含要评估的函数的类与代码当前所在的类位于同一类中。如果要在所述类之外使用方法,则需要更改程序集。

FindTypesMarkedWithDynamicLinqTypeAttribute(new[] { typeof(AnotherClassName).Assembly })

上面的解决方案没有任何变化,这只是为了澄清任何尝试使用它的人。

【讨论】:

    【解决方案5】:

    我可能会感到困惑,但是您在 Selects 中使用字符串的语法对我来说并不编译。以下语法有效:

    var q = b.AsQueryable().Select(it => A.Test(it.Item1));
    

    【讨论】:

    【解决方案6】:
    var b = new[]{ a };
    

    上面的数组不知道是什么类型的数组,不是类型安全的?

    您的值以变体数据类型分配,因此它不是整数值(我认为是字符串值),当您在查询中获取此值时必须需要 convert.toint32() 因为您的类参数数据类型是整数

    请尝试一下

     var b = new **int**[]{ a }; 
    

    而不是var b = new[]{ a };

    重要提示在这里(粗体):

    No property or field 'xxx' exists in **type** 'xxx'
    

    请查看此内容以了解之前的讨论:

    Dynamic Linq - no property or field exists in type 'datarow'

    【讨论】:

      【解决方案7】:

      以下对我有用:

      var a = new Tuple<int, int>(1, 1);
      var b = new[] { a };
      var q = b.AsQueryable().Select(it=>A.Test(it.Item1));
      var q1 = b.AsQueryable().Select(it => Convert.ToInt32(it.Item1));
      var q2 = b.AsQueryable().Select(it => (float) it.Item1);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-06
        • 2018-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多