I am trying to chain multiple compiled linq queries together. I have succeeded in chaining two queries together, but I cannot get a chain of three to work correctly. So here is a reduction of my code to recreate the issue. My two questions are: ‘Why isn’t this working?’ and ‘Is there a better way to keep the performance benefit of compiled queries and also avoid duplication of base query logic that is commonly used?’
Define the following two queries:
Func<DataContext, IQueryable<User>> selectUsers =
CompiledQuery.Compile(
(DataContext dc)=>dc.Users.Select(x=>x)
);
//
Func<DataContext, string, IQueryable<User>> filterUserName =
CompiledQuery.Compile(
(DataContext dc, string name) =>
selectUsers(dc).Where(user=>user.Name == name)
);
Calling and enumerating the chain works fine:
filterUserName(new DataContext(), "Otter").ToList();
Add a third query to the chain:
Func<DataContext, string, int, IQueryable<User>> filterUserAndGroup =
CompiledQuery.Compile(
(DataContext dc, string name, int groupId) =>
filterUserName(dc, name).Where(user=>user.GroupId == groupId)
);
Calling the chain does not work:
filterUserAndGroup(new DataContext(), "Otter", 101);
System.InvalidOperationException:
Member access ‘String Name’ of ‘User’
not legal on type
‘System.Linq.IQueryable1[User].. at1
System.Data.Linq.SqlClient.SqlMember.set_Expression(SqlExpression
value) at
System.Data.Linq.SqlClient.SqlFactory.Member(SqlExpression
expr, MemberInfo member) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.AccessMember(SqlMember
m, SqlExpression expo) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitMember(SqlMember
m) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression
expr) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitBinaryOperator(SqlBinary
bo) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression
expr) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect
select) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias
a) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource
source) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect
select) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitIncludeScope(SqlIncludeScope
scope) at
System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode
node) at
System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode
node) at
System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape
resultShape, Type resultType, SqlNode
node, ReadOnlyCollection
parentParameters, SqlNodeAnnotations
annotations) at
System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression
query, SqlNodeAnnotations annotations)
at
System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Compile(Expression
query) at
System.Data.Linq.CompiledQuery.ExecuteQuery(DataContext
context, Object[] args) at
System.Data.Linq.CompiledQuery.Invoke(TArg0
arg0, TArg1 arg1) at TestMethod() in
….
Looks like you need to convert your first compiled query to a list before executing the second one. In theory that should have caused an error with your two queried chain as well.
From MSDN CompiledQuery:
Perhaps this code will fix it although that could have implications for the roundtrips back to the database if you’re using LINQ to SQL.