Reputation: 10800
Sorry for the bad title...This one is probably best explained with an example:
void Main()
{
IQueryable<ClassA> toLinkTo = context.ClassAs.Where(a => a.Name == "SomeName");
var queryToExecute = context.ClassBs.Where(cb => toLinkTo.Select(ca => ca.Id).Contains(cb.Id));
// This works.
leafNodesWithExternalChildren.ToList();
// This doesn't work.
toLinkTo = new OtherClass(context).LinkedClassAs;
leafNodesWithExternalChildren.ToList();
}
public class OtherClass
{
private MyContext m_Context;
public OtherClass(MyContext ctx)
{
this.m_Context = ctx;
}
public IQueryable<ClassA> LinkedClassAs
{
get
{
// Same as toLinkTo as it was originally declared above.
return this.m_Context.ClassAs.Where(a => a.Name == "SomeName");
}
}
}
How come this works when toLinkTo
is declared locally, but using the exact same IQueryable
as a property on another object doesn't? The exception I get is:
Unable to create a constant value of type 'ClassA'. Only primitive types ('such as Int32, String, and Guid') are supported.
Thanks in advance.
Upvotes: 3
Views: 1602
Reputation: 177163
Your second example works too - if you make it the first example:
IQueryable<ClassA> toLinkTo = new OtherClass(context).LinkedClassAs;
var queryToExecute = context.ClassBs.Where(cb => toLinkTo.Select(ca => ca.Id)
.Contains(cb.Id));
// Now, this works.
queryToExecute.ToList();
// Now, this doesn't work.
toLinkTo = context.ClassAs.Where(a => a.Name == "SomeName");
queryToExecute.ToList();
Somwhow, in the second try EF executes the toLinkTo
query upfront and separately (as if it would append an AsEnumerable()
to the query) to create a collection of objects in memory first. The queryToExecute
doesn't work with this collection - as explained in @mellamokb's answer. In the first try the query is executed as a whole and the problem does not occur.
Both examples work if you create a new queryToExecute2
variable for the second example:
IQueryable<ClassA> toLinkTo = context.ClassAs.Where(a => a.Name == "SomeName");
var queryToExecute = context.ClassBs.Where(cb => toLinkTo.Select(ca => ca.Id)
.Contains(cb.Id));
// this works.
queryToExecute.ToList();
// And this works too.
toLinkTo = new OtherClass(context).LinkedClassAs;
var queryToExecute2 = context.ClassBs.Where(cb => toLinkTo.Select(ca => ca.Id)
.Contains(cb.Id));
queryToExecute2.ToList();
It has probably to do with the way how the expression tree for queryToExecute
is built and how it uses the local variable toLinkTo
or how EF evaluates the tree, but it's beyond my horizon to really understand or explain what's exactly going on.
Edit
Even if you use exactly the same query for toLinkTo
the second try doesn't work:
IQueryable<ClassA> toLinkTo = context.ClassAs.Where(a => a.Name == "SomeName");
var queryToExecute = context.ClassBs.Where(cb => toLinkTo.Select(ca => ca.Id)
.Contains(cb.Id));
// this works.
queryToExecute.ToList();
// this doesn't work.
toLinkTo = context.ClassAs.Where(a => a.Name == "SomeName");
queryToExecute.ToList();
Upvotes: 3