0

EDIT: I managed to reduce my code to the least-common-denominator with the assistance of LinqPad.

I have a method that returns a complex LinqKit predicate. The desired result is to be able to reuse this method whenever I need to perform this query. It works fine in a Where clause against an IQueryable collection of these entities, but I can't for the life of me figure out how to use this predicate when I have this entity as a single property of an object, and want to use predicates to query the other properties of that entity.

I'll give you a simplified example of what I mean.

// Note: .Includes removed for brevity's sake.
var task = Tasks.First(t => t.QaConfigTaskId == 2);

var predicate = PredicateBuilder.True<Task>();

// Ensure that the task's own properties match.
predicate = predicate.And(t => t.TaskType == task.TaskType &&
      t.Description == task.Description &&
      t.PreTreatment == task.PreTreatment &&
      t.Treatment == task.Treatment);

var structureAnalysis = task.StructureAnalysis;
var query = PredicateBuilder.True<StructureAnalysis>();
query = query.And(analysis =>
        // The names match
        analysis.Name == structureAnalysis.Name &&
        // We have the same # of goals so they must all match.
        analysis.Goals.Count == structureAnalysis.Goals.Count
    );
predicate = predicate.And(t => query.Invoke(t.StructureAnalysis));

This will work fine:

StructureAnalyses.AsExpandable().Where(query).Dump();

...assuming StructureAnalyses is an IQueryable of my StructureAnalysis objects from Entity Framework.

But let's say I want to get the Tasks, filtering with the related StructureAnalyses. How can I do such a thing? Keep in mind that in reality, query is built out by a reusable function, and predicate is built out in a separate function that calls it, so I can't simply merge the two queries.

It compiles but fails when I try and execute the query:

Tasks.AsExpandable().Where(predicate).Dump();

...with "The parameter 't' was not bound in the specified LINQ to Entities query expression.", or the like. In this example, Tasks is an IQueryable that contains all of the Task type entities in my DB.

I've also tried altering this line:

        predicate = predicate.And(t => query.Invoke(t.StructureAnalysis));

...to:

        compiled = query.Compile();
        predicate = predicate.And(t => compiled(t.StructureAnalysis));

That also compiles but fails with, "Unable to cast object of type 'System.Linq.Expressions.FieldExpression' to type 'System.Linq.Expressions.LambdaExpression'." and understandably so.

I've also tried calling Expand on both query and compiled, which had no effect, as well as the following "solutions":

Community
  • 1
  • 1
Grinn
  • 5,370
  • 38
  • 51
  • 1
    I think the proposed close reason is invalid. – jamesSampica Jan 26 '15 at 19:29
  • @Shoe The code provides doesn't reproduce the problem; the close reason is entirely valid. Or were you able to reproduce the problem described using the code provided? – Servy Jan 26 '15 at 21:37
  • @Servy I updated the Q some time ago and pinged you in the comments to your answer. I'm pretty sure you'll be able to reproduce it now, but let me know if you can't. – Grinn Jan 26 '15 at 21:44
  • @Grinn The code currently shown does not reproduce the problem. – Servy Jan 26 '15 at 21:47
  • @Servy The code shown is the code in its entirety as executed in LINQPad. No worries though as I've found a solution to the issue at hand. – Grinn Jan 27 '15 at 14:51

1 Answers1

0

I ended up finding the solution on my own. The trick is to wrap the invocation of query in another predicate that accesses the property, then expand that predicate when calling it. So for my example where I'm attempting to invoke a query on a single StructureAnalysis, my code now looks like this:

// Note: .Includes removed for brevity's sake.
var task = Tasks.First(t => t.QaConfigTaskId == 2);

var predicate = PredicateBuilder.True<Task>();

// Ensure that the task's own properties match.
predicate = predicate.And(t => t.TaskType == task.TaskType &&
      t.Description == task.Description &&
      t.PreTreatment == task.PreTreatment &&
      t.Treatment == task.Treatment);



var structureAnalysis = task.StructureAnalysis;
var query = PredicateBuilder.True<StructureAnalysis>();
query = query.And(analysis =>
        // The names match
        analysis.Name == structureAnalysis.Name &&
        // We have the same # of goals so they must all match.
        analysis.Goals.Count == structureAnalysis.Goals.Count
    );

//// HERE'S WHAT'S NEW ////

Expression<Func<Task, bool>> subPredicate = t => query.Invoke(t.StructureAnalysis);

predicate = predicate.And(subPredicate.Expand());

Tasks.AsExpandable().Where(predicate).Dump();
Grinn
  • 5,370
  • 38
  • 51