tags:

views:

92

answers:

3

I have a interface resembling the following:

public IEnumerable<IDocument> Search(Predicate<IDocument> predicate) { ... }

where an IDocument looks like this:

public interface IDocument 
{
    string Id {get; set;}
    string Title {get; set;}
    ...
}

I am about to implement a document provider where I have to decompose the content of the Predicate and extract the properties in order to make an SQL string for a document database to which we currently have no OR/M mapping. An example: I want to know if the user is searching by ID or by Title so I know if I can search by ID or have to perform a LIKE search on Title.

I've always used LINQ heavily but this is the first time I'm sitting in the other end of the pipeline... I've read up a bit on Expression trees and Reflection but the pieces haven't fallen in place yet. How (if?) can I decompose/reflect the Predicate so I get a list of parameters which I can concactate into an SQL string? I am looking for something like this rough sketch:

public IEnumerable<IDocument> Search(Predicate<IDocument> predicate) 
{ 
    string id = DoSomeMagic(predicate, "Id");
    string title = DoSomeMagic(predicate, "Title");
    if (!string.isNullOrEmpty(id))
       ...make ID search
    else
       ...search by title
}

Disclaimer: The provider is for an inhouse legacy system bound for replacement this fall which makes inline SQL a proper choice ;o)

+1  A: 

You can't do this with predicates or delegates. They are compiled and it's not possible to get information about what it does back. You really need Expression trees for this, but parsing this yourself can be difficult. You'd be better of using an O/RM tool.

When you use a Expression together with LINQ to SQL for instance, building your search method will be a breeze:

public IEnumerable<Document> Search(
    Expression<Func<Document, bool>> predicate) 
{
    using (db = new DocumentDataContext())
    {
        return db.Documents.Where(predicate).ToArray();
    }
}
Steven
+1  A: 

You can't easily introspect the predicate. Maybe you should consider changing your design into a more ad hoc predicate type, a more specific predicate interface :

    public interface IDocument 
    {
        string Id {get; set;}
        string Title {get; set;}
    }
    public class SearchCriteria
    {
        public Nullable<int> Id;
        public string Title;
    }
    public IEnumerable<IDocument> Search(SearchCriteria predicate)
    {
        if (predicate.Id.HasValue)
            //...make ID search
        else if (!string.IsNullOrEmpty(predicate.Title))
            //...search by title
        else
            // other kind of search
    }

Replace the public fields with attributes, put the search logic in the SearchCriteria (e.g. .GetResult() or .GetSQLQuery()) and this may fit your system, assuming you can know all the available search criteria.

Seb
That's what we've decided to do. The expression tree we should build would become extremely complex so we've woved against a predicate and uses a more KISS approach.
A: 

You need to declare your method as accepting an Expression> instead. This Expression can be introspected (although there's lots of different things it can be).

Try running the following in LinqPad (LinqPad provides the Dump extension method, which visualises the expression really well)

void Main()
{
    show(x=>x.Length==5);
}

void show(Expression<Predicate<string>> e){
    e.Dump();
}

You can execute an expression by calling Compile first

Rob Fonseca-Ensor