NotSupportedException using ToList() in LINQ and EntityFramework - c#

I have a query that looks like this:
var q = from x in db.Table1
where x.Timestamp.CompareTo(fromDate) >= 0
&& x.Timestamp.CompareTo(toDate) < 0
select x;
var q_list = q.ToList(); // this works fine
var g = q.Where(z=> z.Table2.Equals(ns)); // ns is instance of Table2 not Table1
var g_list = g.ToList(); // this throws exception
The exception:
**
System.NotSupportedException was unhandled
Message=Unable to create a constant value of type ...
I have a lot of functions like that and I do not want to repeat queries, I need to use the Where, GroupBy ...etc and other functions.
FULL exception trace:
**
System.NotSupportedException was unhandled
Message=Unable to create a constant value of type 'DataAccessLayer.Model.Table2. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
Source=System.Data.Entity
StackTrace:
at System.Data.Objects.ELinq.ExpressionConverter.ConstantTranslator.TypedTranslate(ExpressionConverter parent, ConstantExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Objects.ELinq.ExpressionConverter.Convert()
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.Data.Entity.Internal.Linq.InternalQuery`1.GetEnumerator()
at System.Data.Entity.Infrastructure.DbQuery`1.System.Collections.Generic.IEnumerable<TResult>.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
**

The problem is that EntityFramework doesn't understand types that are not primitive. What you have to do is this:
var g = q.Where(z => z.field.Id.Equals(ns.Id));
Keep in mind that if you have a custom implementation of Equals, there's no way it can be translated to proper t-sql. If you want to execute that implementation anyway you'll need to do it in memory:
// note it's using q_list instead of q
var g = q_list.Where(z => z.field.Equals(ns));

Related

ToList thows Exception when building Expression tree using concat multiple Expressions

I'm building a routine to concatenate several entities in order to filter by a string. I create my expressions using the following code, which works properly because I use the same code to sort.
private void SetExpression<T>(string property, ref Type type, ref ParameterExpression arg, ref Expression expr)
{
string[] props = property.Split('.');
foreach (string prop in props)
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
}
I then use 2 more methods to build the tree. I use GenerateConcat to make the concatenation portion.
private Expression<Func<T, string>> GenerateConcat(ParameterExpression parameterExpression, params Expression[] expressions)
{
var parameter = parameterExpression;
var separator = Expression.Constant(" ");
var concatArgs = expressions;
var concatCall = Expression.Call(typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }), concatArgs);
return Expression.Lambda<Func<T, string>>(concatCall, parameter);
}
And I use GenerateContains to create the contains method.
private Expression<Func<T, bool>> GenerateContains(Expression<Func<T, string>> member, string value)
{
var containsCall = Expression.Call(member.Body, "Contains", Type.EmptyTypes, Expression.Constant(value));
return Expression.Lambda<Func<T, bool>>(containsCall, member.Parameters);
}
When run, all appears well and my predicate looks like it is formed properly. However, when ToList() is called the following exception is thrown.
System.InvalidOperationException: The parameter 'x' was not bound in the specified LINQ to Entities query expression.
Result StackTrace:
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ParameterTranslator.TypedTranslate(ExpressionConverter parent, ParameterExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.ConvertToString(ExpressionConverter parent, Expression linqExpression)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.<>c__DisplayClass1c.<ConcatArgs>b__1a(Expression arg)
at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.ConcatArgs(ExpressionConverter parent, Expression linq, Expression[] linqArgs)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.StringConcatTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateFunctionIntoLike(MethodCallExpression call, Boolean insertPercentAtStart, Boolean insertPercentAtEnd, Func`5 defaultTranslator)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.StringContainsTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Triton.Repository.Test.UnitTest1.ComplexTypeTest() in C:\BitBucket\triton-mvc-5\Triton\Triton.Repository.Test\UnitTest1.cs:line 56
Result Message:
Test method Triton.Repository.Test.UnitTest1.ComplexTypeTest threw exception:
System.InvalidOperationException: The parameter 'x' was not bound in the specified LINQ to Entities query expression.
I'm guessing but the problem is how I'm building the Concat portion (GenerateConcat), but I can't seem to figure out what's wrong. Am I missing anything perhaps with respect to the ParameterExpression? I made sure it is the same for all the Expressions. Thanks in advance.
Edit 1: Here's the code that creates the expression
I use the Initialize block to create the expression based off of the property name. Then I use the Append method to get the expression from the other properties. My error was when I created the sub properties I didn't properly assign the ParameterExpression. So I overloaded Intialize and added a ParamerterExpression parameter to keep the expression the same for all sub properties. That solved it.
public ExpressionBuilder Initialize<T>(string property)
{
if (string.IsNullOrEmpty(property))
throw new ArgumentNullException(property);
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
SetExpression<T>(property, ref type, ref arg, ref expr);
ExpressionModel = new ExpressionModel() { Expression = expr, ParameterExpression = arg, Type = type };
IsIntialized = true;
return this;
}
public ExpressionBuilder Append<T>(string property)
{
if (string.IsNullOrEmpty(property))
throw new ArgumentNullException(property);
if (IsIntialized == false)
throw new Exception("ExpressionBuilder has not been initialized.");
Type type = ExpressionModel.Type;
ParameterExpression arg = ExpressionModel.ParameterExpression;
Expression expr = ExpressionModel.Expression;
SetExpression<T>(property, ref type, ref arg, ref expr);
ExpressionModel.Expression = expr;
ExpressionModel.ParameterExpression = arg;
ExpressionModel.Type = type;
return this;
}
The ParameterExpression for the sub properties have to be the same as the parent. So I overloaded the function that creates Expression in order to be able to set the ParameterExpression explicitly.
public ExpressionBuilder Initialize<T>(string property, ParameterExpression parameterExpression)
{
if (string.IsNullOrEmpty(property))
throw new ArgumentNullException(property);
Type type = typeof(T);
ParameterExpression arg = parameterExpression;
Expression expr = arg;
SetExpression<T>(property, ref type, ref arg, ref expr);
ExpressionModel = new ExpressionModel() { Expression = expr, ParameterExpression = arg, Type = type };
IsIntialized = true;
return this;
}

Multisorting IQueryable using a string column name within a generic extension method?

I need to sort collection by a few columns. I have list of objects where every object contain columnName and sortDir(Asc or Desc). So, how i can do it? I have tried to do it by reflection, but i have the error:
Message: `LINQ to Entities does not recognize the method 'Int32 Parse(System.String)' method, and this method cannot be translated into a store expression.`
StackTrace: ` at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.DefaultTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()`
I read some articles this and this, but their solutions doesn't help me. Maybe somebody have some ideas for me?
Code:
public static IQueryable<T> SortByParams<T>(IQueryable<T> collection, List<SortBy> sorting)
{
bool firstOrder = true;
IOrderedQueryable<T> sortedRes = null;
foreach (SortBy sort in sorting)
{
try
{
if (firstOrder)
{
if (sort.sortDir.ToLower() == "desc")
{
sortedRes = collection.OrderByDescending(obj => obj.GetType().GetProperty(sort.columnName).GetValue(obj, null));
}
else
{
sortedRes = collection.OrderBy(obj => obj.GetType().GetProperty(sort.columnName).GetValue(obj, null));
}
firstOrder = false;
}
else
{
if (sort.sortDir.ToLower() == "desc")
{
sortedRes = sortedRes.ThenByDescending(obj => obj.GetType().GetProperty(sort.columnName).GetValue(obj, null));
}
else
{
sortedRes = sortedRes.ThenBy(obj => obj.GetType().GetProperty(sort.columnName).GetValue(obj, null));
}
}
}
catch (Exception) { }
collection = sortedRes;
}
return collection;
}
Expressions like this
obj => obj.GetType().GetProperty(sort.columnName).GetValue(obj, null)
might be applicable in LINQ To Objects context, but definitely not in LINQ to Entities.
In general you cannot use reflection (and many other methods) inside the LINQ to Entities query expression because they cannot be translated to SQL (or query provider does not recognize and handle them, hence NotSupportedException).
Instead of reflection, you should build the expression using System.Linq.Expressions.
For instance, like this:
public static IQueryable<T> SortByParams<T>(this IQueryable<T> source, List<SortBy> sorting)
{
var queryExpr = source.Expression;
string methodAsc = "OrderBy";
string methodDesc = "OrderByDescending";
foreach (var item in sorting)
{
var selectorParam = Expression.Parameter(typeof(T), "e");
var selector = Expression.Lambda(Expression.PropertyOrField(selectorParam, item.columnName), selectorParam);
var method = string.Equals(item.sortDir, "desc", StringComparison.OrdinalIgnoreCase) ? methodDesc : methodAsc;
queryExpr = Expression.Call(typeof(Queryable), method,
new Type[] { selectorParam.Type, selector.Body.Type },
queryExpr, Expression.Quote(selector));
methodAsc = "ThenBy";
methodDesc = "ThenByDescending";
}
return source.Provider.CreateQuery<T>(queryExpr);
}

Entity framework order by expression

I have encountered an issue with a ExtJS grid where I enabled remote filtering, sorting and grouping.
System.NotSupportedException: Unable to cast the type 'System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]' to type 'System.Object'. LINQ to Entities only supports casting EDM primitive or enumeration types.
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ValidateAndAdjustCastTypes(TypeUsage toType, TypeUsage fromType, Type toClrType, Type fromClrType)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.GetCastTargetType(TypeUsage fromType, Type toClrType, Type fromClrType, Boolean preserveCastForDateTime)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.CreateCastExpression(DbExpression source, Type toClrType, Type fromClrType)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ConvertTranslator.TranslateUnary(ExpressionConverter parent, UnaryExpression unary, DbExpression operand)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.UnaryTranslator.TypedTranslate(ExpressionConverter parent, UnaryExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.UnarySequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SelectTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
bij Systm.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
bij System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
bij System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
bij System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClassc.<GetResultsAsync>b__a()
bij System.Data.Entity.Core.Objects.ObjectContext.<ExecuteInTransactionAsync>d__3d`1.MoveNext()
The code sample below converts the input model (string properties indicating direction and property name) and generates a LINQ script on the fly, which will be used in the repository pattern, and consequently also Entity Framework. This script works well for non-nullable types, but it gives the error above when nullable properties are being sorted.
Expression<Func<Task, object>> orderByClause = default(Expression<Func<Task, object>>);
if (sortObjects != null)
{
foreach (Order sortObject in sortObjects)
{
// Get type and property of type
ParameterExpression parameter = Expression.Parameter(typeof(Task), "x");
PropertyInfo property = typeof(Task).GetProperty(sortObject.Property);
// Create left hand side of the lambda: x => x.PROPERTY
MemberExpression propertyAccess = Expression.Property(parameter, property);
LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);
// Create expression from lambda
MemberExpression orderByExpression = Expression.Property(parameter, sortObject.Property);
Expression conversion = Expression.Convert(orderByExpression, typeof(object));
Expression<Func<Task,object>> lambda = Expression.Lambda<Func<Task, object>>(conversion, parameter);
if (orderByClause == default(Expression<Func<Task, object>>))
{
orderByClause = lambda;
}
else
{
InvocationExpression invokedExpr = Expression.Invoke(lambda, orderByClause.Parameters.Cast<Expression>());
orderByClause = Expression.Lambda<Func<Task, object>>(Expression.AndAlso(orderByClause.Body, invokedExpr), orderByClause.Parameters);
}
}
}
return orderByClause;
The issue here probably is the casting between the object and the nullable type. I'd need to be able to box the nullable type in the expression.
I was thinking of two options here:
1) Use some kind of generic method to define the property type instead of using the object type
2) Box the nullable property so it can be casted to an object.
I think option 2 is the easiest but I'm kind of stuck here.
Update:
I still haven't found a solution for this but I have a workaround until I have solved this issue. Now the sorting key isn't an object anymore, instead it's a generic type. This scenario will always work because there isn't any boxing occurring anymore:
protected virtual Expression<Func<T, TKey>> GetSorting<TKey>(string ordering)
{
IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);
Expression<Func<T, TKey>> orderByClause = default(Expression<Func<T, TKey>>);
if (sortObjects != null)
{
foreach (Order sortObject in sortObjects)
{
// Get type and property of type
ParameterExpression parameter = Expression.Parameter(typeof(T), "x");
PropertyInfo property = typeof(T).GetProperty(sortObject.Property);
// Create left hand side of the lambda: x => x.PROPERTY
MemberExpression propertyAccess = !sortObject.ComplexType ? Expression.Property(parameter, property) : Expression.PropertyOrField(Expression.Property(parameter, property), sortObject.ComplexTypeProperty);
MemberExpression orderByExpression = !sortObject.ComplexType ? Expression.Property(parameter, sortObject.Property) : Expression.PropertyOrField(Expression.Property(parameter, property), sortObject.ComplexTypeProperty);
// Create expression from lambda
Expression<Func<T, TKey>> lambda = Expression.Lambda<Func<T, TKey>>(orderByExpression, parameter);
if (orderByClause == default(Expression<Func<T, TKey>>))
{
orderByClause = lambda;
}
else
{
InvocationExpression invokedExpr = Expression.Invoke(lambda, orderByClause.Parameters.Cast<Expression>());
orderByClause = Expression.Lambda<Func<T, TKey>>(Expression.AndAlso(orderByClause.Body, invokedExpr), orderByClause.Parameters);
}
}
}
return orderByClause;
}
Here's how I call this method. From the user's input, I determine which property is being sorted. Using reflection, I retrieve the its type, which I then pass as the type for the sorting method.
switch (propertyType)
{
case "Int32":
Expression<Func<Resource, int?>> orderIntExpression = this.GetSorting<int?>(ordering);
return await this.GetResources<int?>(page, pageSize, orderIntExpression, base.IsAscending(ordering), selector, mergedQuery);
default:
Expression<Func<Resource, object>> orderDefaultExpression = this.GetSorting<object>(ordering);
return await this.GetResources<object>(page, pageSize, orderDefaultExpression, base.IsAscending(ordering), selector, mergedQuery);
}
This works but it's not very scalable in my opinion. I don't see any easy or pretty fixes here to dynamically pass the type to the sorting method. Any suggestions on this matter?
Again after a lot of trial and error I have rescued myself by writing the code below. It is not complete yet but this allows sorting and grouping nullable types.
Expression<Func<T, dynamic>> sortExpression = default(Expression<Func<T, dynamic>>);
IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);
if (sortObjects != null)
{
foreach (Order sortObject in sortObjects)
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
MemberExpression propertyReference = Expression.Property(parameter, sortObject.Property);
Expression conversion = Expression.Convert(propertyReference, typeof(object));
sortExpression = Expression.Lambda<Func<T, dynamic>>(conversion, new[] { parameter });
break;
}
}
return sortExpression;

EntityFramework LINQ string order comparison

I have a varchar(5) column (named name) in my table (named codes). This SQL query is what I'd like to accomplish via EF, without first converting my IQueryable to IEnumerable (in memory):
SELECT * FROM codes WHERE name >= 'J0000' AND name <= 'J9999'
I've tried performing a query using this method, like this:
var results = db.Codes.Where(c=>c.Name.CompareTo("J0000") >=0
&& c.Name.CompareTo("J9999") <=0)
However the CompareTo can't be translated into sql and an exception is thrown. In order for CompareTo to work, you have to have an IEnumerable, meaning all the records are already pulled from the database into memory. When try to execute this I get:
{"The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities."}
With stack trace:
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NotSupportedTranslator.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
My only other option that I can think of is to use .SqlQuery() on my db.Codes DbSet in order to execute the actual select statement using >= and <=.
Are there any non-sql options that will allow me to perform a query like this?
EDIT: In my actual code I was doing a .CompareTo(codes[0]) where codes was an array of string. This array was breaking the EF sql translation. My solution was to get the string value into a new string variable first, and then pass the new string variable to .CompareTo() instead of the array variable / index specified.
You can use CompareTo to compare your strings. Entity Framework converts these into >, <, >= and <=.
var results = db.Codes.Where(c => c.Name.CompareTo("J0000") >= 0);
Or using this syntax:
var results = from c in db.Codes
where c.Name.CompareTo("J0000") >= 0
select c;
This will produce SQL output similar to this:
WHERE [Extent1].[Name] >= N'J0000'
EDIT
After you gave your error message it seems you are using an indexed property instead of string literals in your Linq query. To fix this, copy the values to temporary variables. So this:
var results = db.Codes.Where(c=> c.Name.CompareTo(somearray[0]) >=0
&& c.Name.CompareTo(somearray[1]) <=0)
Becomes this:
var lowerBound = somearray[0];
var upperBound = somearray[1];
var results = db.Codes.Where(c=> c.Name.CompareTo(lowerBound) >=0
&& c.Name.CompareTo(upperBound) <=0)

Entity Framework throws NotSupportedException after calling Any or Count

I've got the following code:
private void Import(DbSet<DBEntity> dbEntities, IEnumerable<ExcelEntity> entities, ClapEntities context)
{
foreach (var me in entities)
{
try
{
var dbe = dbEntities.Where(IsEqualRecord(me, context));
bool hasElement = dbe.Any(); //Line which throws the exception
}
catch (Exception ex)
{
//...
}
}
}
protected abstract Expression<Func<DBEntity, bool>> IsEqualRecord(ExcelEntity entity, ClapEntities context);
The Any() extension throws a NotSupportedException:
System.Exception: Could not map entity to database. Mapper: [CuttingToolImport]; Rownumber: [153] ---> System.NotSupportedException: The method 'First' can only be used as a final query operation. Consider using the method 'FirstOrDefault' in this instance instead.
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.FirstTranslator.TranslateUnary(ExpressionConverter parent, DbExpression operand, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.EqualsTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.AggregateTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery1.<>c_DisplayClassb.b_a()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery1.<>c_DisplayClassb.b_9()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery1..GetEnumerator>b__0()
at System.Lazy1.CreateValue()
at System.Lazy1.LazyInitValue()
at System.Data.Entity.Internal.LazyEnumerator1.MoveNext()
at System.Linq.Enumerable.Single[TSource](IEnumerable1 source)
at System.Linq.Queryable.Count[TSource](IQueryable1 source)
at CLAP.Models.Import.ImportMapping.ImportBase2.Import(DbSet1 dbEntities, IEnumerable1 entities, ClapEntities context)
--- End of inner exception stack trace ---
As you can see, the Any() extension (or the Count() extension) uses the First() extension internal which is not allowed. I have no idea how I can fix that error.
Exactly this code has worked for many months with Entity Framework version 5 or 6 and
Microsoft SQL Server 2012 - 11.0.2100.60 (X64) Express Edition (64-bit) on
Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
Does anyone has an idea on how to solve that error?
You should use .FirstOrDefault() instead which will return NULL when there is no entities in dataset:
bool hasElement = dbe.FirstOrDefault() != null;
Another possible issue in your code is that you're using custom IsEqualRecord method which should contain only code that can be translated into SQL.

Categories

Resources