简体   繁体   English

动态Linq - 对具有“动态”类型成员的对象执行查询

[英]Dynamic Linq - Perform a query on an object with members of type “dynamic”

I am trying to use a dynamic linq query to retrieve an IEnumerable<T> from an object collection (Linq to Object), each of the objects in the collection have an internal collection with another set of objects where the data is stored, these values are accessed through an indexer from the outer collection 我试图使用动态linq查询从对象集合(Linq到Object)中检索IEnumerable <T>,集合中的每个对象都有一个内部集合,其中包含存储数据的另一组对象,这些值通过外部集合中的索引器访问

The dynamic linq query returns the filtered set as expected when you are working with strongly typed objects but my object stores the data in a member of type dynamic , please see the example below: 当您使用强类型对象但我的对象将数据存储在动态类型的成员中时,动态linq查询会按预期返回已过滤的集,请参阅下面的示例:

public class Data
{
    public Data(string name, dynamic value)
    {
        this.Name = name;
        this.Value = value;
    }

    public string Name { get; set; }
    public dynamic Value { get; set; }
}

public class DataItem : IEnumerable
{
    private List<Data> _collection;

    public DataItem()
    { _collection = new List<Data>(); }

    public dynamic this[string name]
    {
        get
        {
            Data d;
            if ((d = _collection.FirstOrDefault(i => i.Name == name)) == null)
                return (null);

            return (d.Value);
        }
    }

    public void Add(Data data)
    { _collection.Add(data); }

    public IEnumerator GetEnumerator()
    {
        return _collection.GetEnumerator();
    }
}

public class Program
{
    public void Example()
    {
        List<DataItem> repository = new List<DataItem>(){
            new DataItem() {
                new Data("Name", "Mike"),
                new Data("Age", 25),
                new Data("BirthDate", new DateTime(1987, 1, 5))
            },
            new DataItem() {
                new Data("Name", "Steve"),
                new Data("Age", 30),
                new Data("BirthDate", new DateTime(1982, 1, 10))
            }
        };

        IEnumerable<DataItem> result = repository.AsQueryable<DataItem>().Where("it[\"Age\"] == 30");
        if (result.Count() == 1)
            Console.WriteLine(result.Single()["Name"]);
    }

When I run the above example I get: Operator '==' incompatible with operand types 'Object' and 'Int32' 当我运行上面的例子时,我得到: 运算符'=='与操作数类型'Object'和'Int32'不兼容

Are dynamic members incompatible with Dynamic Linq queries?, or is there another way of constructing expressions that would evaluate properly when dealing with members of type dynamic 动态成员是否与Dynamic Linq查询不兼容?还是有另一种构造表达式的方法,这些表达式在处理动态类型的成员时会正确评估

Thanks a lot for your help. 非常感谢你的帮助。

Are dynamic members incompatible with Dynamic Linq queries?, or is there another way of constructing expressions that would evaluate properly when dealing with members of type dynamic ? 动态成员是否与Dynamic Linq查询不兼容?还是有另一种构造表达式的方法,这些表达式在处理动态类型的成员时会正确评估?

Both can work together. 两者都可以一起工作。 Just do a conversion to Int32 before doing the comparison like so: 在进行比较之前,只需转换为Int32,如下所示:

IEnumerable<DataItem> result = 
           repository.AsQueryable<DataItem>().Where("Int32(it[\"Age\"]) == 30");

Edit 1: Having said that, the use of dynamic binding in connection with Linq is restricted in general as dynamic operations are not allowed in expression trees. 编辑1:话虽如此,与Linq相关的动态绑定的使用通常受到限制,因为表达式树中不允许动态操作。 Consider the following Linq-To-Objects query: 请考虑以下Linq-To-Objects查询:

IEnumerable<DataItem> result = repository.AsQueryable().
                                                  Where(d => d["Age"] == 30);

This code snippet won't compile for the reason mentioned above. 由于上述原因,此代码段无法编译。

Edit 2: In your case (and in conjunction with Dynamic Linq) there are some ways to hack yourself around the issues mentioned in Edit 1 and in the original question. 编辑2:在您的情况下(与Dynamic Linq一起),有一些方法可以解决编辑1和原始问题中提到的问题。 For example: 例如:

// Variant 1: Using strings all the way
public void DynamicQueryExample(string property, dynamic val)
{
   List<DataItem> repository = new List<DataItem>(){
        new DataItem() {
            new Data("Name", "Mike"),
            new Data("Age", 25),
            new Data("BirthDate", new DateTime(1987, 1, 5))
        },
        new DataItem() {
            new Data("Name", "Steve"),
            new Data("Age", 30),
            new Data("BirthDate", new DateTime(1982, 1, 10))
        }
    };

    // Use string comparison all the time        
    string predicate = "it[\"{0}\"].ToString() == \"{1}\"";
    predicate = String.Format(whereClause , property, val.ToString());

    var result = repository.AsQueryable<DataItem>().Where(predicate);
    if (result.Count() == 1)
        Console.WriteLine(result.Single()["Name"]);
}

Program p = new Program();

p.DynamicQueryExample("Age", 30); // Prints "Steve"
p.DynamicQueryExample("BirthDate", new DateTime(1982, 1, 10)); // Prints "Steve"
p.DynamicQueryExample("Name", "Mike"); // Prints "Steve" (nah, just joking...)

or: 要么:

// Variant 2: Detecting the type at runtime.
public void DynamicQueryExample(string property, string val)
{
    List<DataItem> repository = new List<DataItem>(){
        new DataItem() {
            new Data("Name", "Mike"),
            new Data("Age", 25),
            new Data("BirthDate", new DateTime(1987, 1, 5))
        },
        new DataItem() {
            new Data("Name", "Steve"),
            new Data("Age", 30),
            new Data("BirthDate", new DateTime(1982, 1, 10))
        }
    };

    string whereClause = "{0}(it[\"{1}\"]) == {2}";


    // Discover the type at runtime (and convert accordingly)
    Type type = repository.First()[property].GetType();
    string stype = type.ToString();
    stype = stype.Substring(stype.LastIndexOf('.') + 1);

    if (type.Equals(typeof(string))) {
        // Need to surround formatting directive with ""
        whereClause = whereClause.Replace("{2}", "\"{2}\"");
    }
    string predicate = String.Format(whereClause, stype, property, val);

    var result = repository.AsQueryable<DataItem>().Where(predicate);
    if (result.Count() == 1)
        Console.WriteLine(result.Single()["Name"]);
}

var p = new Program();
p.DynamicQueryExample("Age", "30");
p.DynamicQueryExample("BirthDate", "DateTime(1982, 1, 10)");
p.DynamicQueryExample("Name", "Mike");

Have you tried it[\\"Age\\"].Equals(object(30)) ? 你试过吗it[\\"Age\\"].Equals(object(30))

Such that: 这样:

IEnumerable<DataItem> result = 
    repository.AsQueryable<DataItem>().Where("it[\"Age\"].Equals(object(30))");

Edit: Updated to correctly cast 30 to object. 编辑:已更新为正确投射30到对象。

Is the code below useful for you? 以下代码对您有用吗?

IEnumerable<DataItem> result = repository.AsQueryable<DataItem>().Where("it[\"Age\"].ToString() == \"30\"");

But for this to work, all your types which can be assigned to the Value member of your Data class needs to have a useful implementation of the ToString method. 但为了实现这一点,可以分配给Data类的Value成员的所有类型都需要具有ToString方法的有用实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM