0

I threw down this code because it worked, but I really need to refactor to something acceptable. It accepts a set of query objects (strings that look like productid = 3) then adds them to my query. This only works for logical AND, but I will eventually need a few different logical operators (OR, NOT).

-- Idea here is add the where clause to the original query and return a new one
private static IQueryable<Product> GetFilteredQuery(string condition,
    IQueryable<Product> originalQuery)
    {
        -- REPETITION 
        if( -- Regex comparison looking for "productid = 123" --)
        {   
            returnQuery = originalQuery.Where(
                    p => p.myEntity.SelectMany(q => q.subEntity) // spec expression
                          .Any(r => r.id == foundid));           
        }
        ... (one if statement for each specification, calling this several times)

I also have this for Ordering:

private static IQueryable<Product> GetOrderedQuery( IList<string> fields,
    IQueryable<Product> originalQuery)
{
    var resultQuery = originalQuery;
    bool firstTime = true;
    foreach( var field in fields)
    {   
        -- REPETITION  
        if( field == "id")
        {    if( firstTime == true)
             {   resultQuery = resultQuery.OrderBy( p => p.id);
                 firstTime = false;
             }
             else
             {   resultQuery = resultQuery.ThenBy( p => p.id);
             }
        }
        ... (one for each field to order by)
    }

So how could I encapsulate each repetition in to a specification object where I can somehow attach this collection of specifications to my original query, including the order expressions? This is under the Linq to Entities, Entity Framework 4, and C# umbrella.

It would be really nice to do something like this, which is essentially what the above does.

var originalQuery = ...;
foreach( var spec in mySpecs)
{    originalQuery = spec(originalQuery);  //adds all the where clauses
}

originalQuery = orderSpec( originalQuery); // adds all the order fields

Links to websites, example code, would surely be appreciated.

Zachary Scott
  • 20,968
  • 35
  • 123
  • 205
  • Am I the only one here missing the point behind these methods? Wouldn't it just be easier to allow the caller to chain together the LINQ calls themselves rather than try to over-engineer something to hide it from them? – Justin Niessner Nov 19 '10 at 02:22
  • The fields are coming from the client. The client is a web browser sending JSON back and forth. I will need to cycle through each provided from the client and implement it. – Zachary Scott Nov 19 '10 at 02:27
  • What about deserializing JSON to .NET object (lets call it search conditions) and then build Linq to entities query for well known search conditions structure? – Ladislav Mrnka Nov 19 '10 at 10:12
  • As long as the expression is strongly typed, you still have to cycle through all the logical "where" criteria from the client and append it to the original query. Seems doable, just need some examples. Sounds like one big if statement to me. LOL – Zachary Scott Nov 20 '10 at 20:55

2 Answers2

1

The only thing I saw something similar was this:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

But that's not specific to EF 4. Why not convert the query to entity SQL?

You can create the query as a string, and append these terms to that SQL query.

HTH.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
  • Only problem with Dynamic SQL is it is not strongly typed. The Specification pattern would be strongly typed, using Lambda expressions to indicate participation. p => p.name – Zachary Scott Nov 30 '10 at 00:06
  • With Dynamic SQL, I agree, you can use Cast to cast to appropriate type. Entity SQL would work. – Brian Mains Dec 03 '10 at 13:27
1

Have a look at LinqSpecs, it might do what you need, or at least give you some ideas to work with.

From what I understand you might be able to do something like:

var originalSpec = ...;
var composedSpec = originalSpec;

foreach(var spec in mySpecs)
{    
    composedSpec &&= spec;  //adds all the where clauses
}

composedSpec &&= orderSpec; // adds all the order fields
stusherwin
  • 1,836
  • 19
  • 19