Reputation: 20686
I have a class that helps project entities to POCOs by emitting Linq expressions that can be used with an IQueryable. Here is an expression I'm having some trouble with:
result = Expression.Bind(dProperty,
Expression.Coalesce(Expression.Property(parameterExpression, sProperty),
Expression.Default(dType)));
In this statement, dProperty
is a PropertyInfo
representing the destination property in the binding, dType
is the Type
of that property, parameterExpression
is another Linq expression representing the parameter of type TEntity
, and sProperty
is a PropertyInfo
object representing the source property.
This builds, but at runtime when the Entity Framework expression visitor implementation works its way through the tree, it sees this and throws a "not supported" exception (below). What seems to be the meaning is that the DefaultExpression I'm using as the right side of the coalescence operator expression isn't supported in EF. I tried searching quite a bit for documentation or discussions that addressed this and couldn't find anything (of course, it's hard to do a very good search on a word like 'default'). Anyway, is it true that the C# 'default' operator is not supported in Linq-to-Entities? If it is true, then how do I do what I'm trying to do, which is to assign a property from a Nullable
whose generic argument type is the same as the destination property?
In other words, the linq expression would look like this if you wrote it as a lambda:
(/*long*/dest, /*long?*/src) => dest = src ?? default(long)
Here's the exception.
System.NotSupportedException: Unknown LINQ expression of type 'Default'.
at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitBinary(BinaryExpression b)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitMemberAssignment(MemberAssignment assignment)
at System.Linq.Expressions.EntityExpressionVisitor.VisitBinding(MemberBinding binding)
at System.Linq.Expressions.EntityExpressionVisitor.VisitBindingList(ReadOnlyCollection`1 original)
at System.Linq.Expressions.EntityExpressionVisitor.VisitMemberInit(MemberInitExpression init)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original)
at System.Linq.Expressions.EntityExpressionVisitor.VisitInvocation(InvocationExpression iv)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitLambda(LambdaExpression lambda)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitUnary(UnaryExpression u)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original)
at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m)
at System.Data.Objects.ELinq.Funcletizer.<>c__DisplayClass5.<Nominate>b__4(Expression exp, Func`2 baseVisit)
at System.Linq.Expressions.EntityExpressionVisitor.BasicExpressionVisitor.Visit(Expression exp)
at System.Data.Objects.ELinq.Funcletizer.Nominate(Expression expression, Func`2 localCriterion)
at System.Data.Objects.ELinq.Funcletizer.Funcletize(Expression expression, Func`1& recompileRequired)
at System.Data.Objects.ELinq.ExpressionConverter..ctor(Funcletizer funcletizer, Expression expression)
at System.Data.Objects.ELinq.ELinqQueryState.CreateExpressionConverter()
at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
at System.Linq.Enumerable.<TakeIterator>d__3a`1.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Collections.Generic.List`1.InsertRange(Int32 index, IEnumerable`1 collection)
at MyClass...
Upvotes: 2
Views: 1171
Reputation: 74530
As you note, LINQ-to-Entities does not support the Expression.Default
method.
However, LINQ-to-Entities does support the Expression.Constant
method.
If you are working with a generic type parameter, you can simply set the second parameter of the coalesce operation be a constant; after all, default(T)
is going to return a constant given the same instance of T
.
The code to do that is as follows:
result = Expression.Bind(dProperty,
Expression.Coalesce(
Expression.Property(parameterExpression, sProperty),
Expression.Constant(default(T), dType),
)
);
I'm assuming you aren't working with generics, so you can't use the default
keyword here. However, you can create a method that will get it for you given a Type
.
(Note, if you don't want to rely on a solution based on the codification of rules outside the code, you can get the result of Expression.Default
on-the-fly with only a Type
instance.)
Once you have that, you can do the following:
result = Expression.Bind(dProperty,
Expression.Coalesce(
Expression.Property(parameterExpression, sProperty),
// Note: The example referenced was changed to an extension method
// on Type.
Expression.Constant(dType.GetDefault(), dType),
)
);
Upvotes: 4