How to use Dynamic LINQ to simultaneously query expressions on several object types

c# dynamic-linq linq

Question

I have been experimenting with Dynamic LINQ as a solution to a scenario, whereby I need to allow a user to select a series of files based upon a "tag" that is assigned to it. E.g.

File A - tagged as "a",
File B - tagged as "b",
File C - tagged as "c",
File D - tagged as "d" and "a"

So if I want to find all files tagged as "a", I would type:

tag = "a"

I would get file a and file d. But I could also type

tag = "a" OR tag = "b"

So I would retrieve file a, b and d

This is just an example by the way!

So, given this scenario, I am able to achieve this by using the following code:

List<FileMeta.Filetag> tags = fetchAllTags(file); //this fetches the tags from the file and places them in a list
            if (tags != null) //just in case
            {
                //declare the type of parameter we are looking for - in this case a "tag"
                var p = Expression.Parameter(typeof(string), "Tag");
                //setup the lambda parser to accept the param type, and use the expression
                var z = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, equation);

                //for each tag in this file
                foreach (FileMeta.Filetag t in tags)
                {
                    //if it hasn't been found yet
                    if (!bFound)
                    {
                        bFound = (bool)z.Compile().DynamicInvoke(t.tag);
                    }
                }
            }

So here I am retrieving the tags from the file, and enumerating them to check to see if they fulfil the equation entered. This is all OK, works fine.

What I have a problem with, is the following scenario.

The "tags" for these files are actually stored in the alternate stream info for a file, along with other information that is effectively "meta" information about the file. A file may have 1 or more pieces of meta info. Because of this, everything is serialised to a single stream with the file as XML data from typed objects - you may have noticed that the List in the above code is of type FileMeta.Filetag. All of the objects inherit from a base object called Filemetabaseobject.

So what I can't get my head around, is if I want to look at a different type of object, e.g. a Filedate object, at the same time as the Filetag object, is it possible with Dynamic LINQ to pass, say, each object in of it's base type (Filemetabaseobject) and somehow do some casting?

I ask, because in a perfect scenario, I would like to be able to query this meta info as, e.g. tag = "a" AND date = "01/01/2012" for example, but the problem I have is that the "tag" would be in a Filetag object, and the "date" would be in a Filedate object obviously as properties: e.g. in a simplistic scenario:

public class Filemetabaseobject
{

}
public class Filetag : Filemetabaseobject
    {
        public string tag { get; set; }
    }
public class Filedate : Filemetabaseobject
{
    public string date { get; set; }
}

Is there a solution to this?

Thanks in advance!

Sorry, I have added, the user can type pretty much any type of query, e.g (tag="a" AND tag="b") OR tag="c" or they may then type (tag="a" AND tag="b" AND tag="c") for example.

1
0
1/5/2012 12:30:22 PM

Accepted Answer

With the help of the suggestion made by sq33G above regarding having abstract classes that expose a value property, I have been able to solve this.

Basically as all types (tags included) inherit from Filemetabaseobject, having an overrideable property called "value" that is set to whatever you want to expose for searching, you can from the inherited object set the get value to retrieve whatever you want that object to be found for.

Then using the following implementation of the Dynamic LINQ ParseLambda method, you can pass the inherited object type in as it's base class equivalent, and it is still able to consult any type of inherited object, based upon being able to see this overridden property:

//If we pass in type of filemetabaseobject, because all file metas inherit from it, and we have exposed an overridde property of "value" to all objects, we can just consult that on all types (where lequation is the string representing the equation):
var z = System.Linq.Dynamic.DynamicExpression.ParseLambda(typeof(FileMeta.Filemetabaseobject), typeof(bool), lequation);
//invoke the query - true will be returned if the query is valid against the property(ies) exposes
bFound = (bool)z.Compile().DynamicInvoke(t); //where t is my filetag that I'm querying against, but could be anything that inherits from filemetabaseobject
0
1/11/2012 9:24:39 AM

Popular Answer

You could give your FileMetabaseObject an abstract read-only string property called Value, then override it in all child classes:

public abstract class FileMetabaseObject {
    public abstract string Value { get; }
}

public class FileTag : FileMetabaseObject {
    public string Tag { get; set; }
    public override string Value { get { return Tag; } }
}

Then you'll be able to use

List<Func<FileMetabaseObject,bool>> whereClauses = 
    new List<Func<FileMetabaseObject,bool>> { 
        m => m.Value == "foo", 
        m => m.Value == DateTime.Today.ToShortDateString() };

bool meetsCriteria = whereClauses.
    All(criteria => fileMetadataColl.
        Any(meta => criteria(meta));

Not quite clear how dynamic Linq is helpful here, though.



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