Perform a query on an object having members of type "dynamic" using Dynamic Linq.

c# dynamic-linq expression-trees lambda

Question

I'm attempting to utilize a dynamic Linq query to obtain an IEnumerableT> from an object collection (Linq to Object). Each object in the collection has an internal collection with a different set of objects where the data is kept.

When dealing with highly typed objects, the dynamic linq query returns the filtered set as anticipated, however my object keeps the data in a member of type dynamic, as seen in the example below:

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 aforementioned example, I get: Incompatibility of the operator "==" with the operand types "Object" and "Int32"

dynamic members and Dynamic Linq queries incompatible? , or is there an other approach to creating expressions that would correctly evaluate when dealing with members of type dynamic

Many thanks for your assistance.

1
7
1/12/2012 2:04:09 AM

Accepted Answer

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?

Both may cooperate. Simply convert to Int32 before doing the comparison as follows:

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

Edit 1: However, since dynamic operations are prohibited in expression trees, the usage of dynamic binding in relation to Linq is often constrained. Take into account the following Linq-To-Objects example:

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

Due to the aforementioned issue, this code fragment won't compile.

Edit 2: There are various ways you can get around the problems indicated in Edit 1 and the original question in your situation (along with Dynamic Linq). For instance:

// 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");
2
1/12/2012 10:52:07 AM

Popular Answer

Are you able to utilize the code below?

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

However, for this to function, all of your types that may be given to theValue part of yourData class must implement the in a practical wayToString method.



Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow