Exception trying to use a basic Expression created with LinqKit - c#

I'm trying to force ef-core to paramerize values that it considers constants as described in this article by using LinqKit.
When trying to apply the PredicateBuilder example described on github, I get an exception. This is my code:
private async Task getWallets()
{
var query =
from w in _context.Wallets.AsExpandable()
where w.WalletItems.Any(hasItems().Compile())
select w;
var result = await query.ToListAsync();
}
private Expression<Func<WalletItem, bool>> hasItems()
{
var predicate = PredicateBuilder.New<WalletItem>();
var ids = new string[] { "A", "B" };
foreach (var id in ids)
{
predicate = predicate.Or(wi => wi.ExternalId == id);
}
return predicate;
}
When hitting ToListAsync() the following exception:
System.NotSupportedException: 'Could not parse expression 'w.WalletItems.Any(__Compile_0)': The given arguments did not match the expected arguments: Object of type 'System.Linq.Expressions.TypedParameterExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.'
I am running the code in an ASP.NET Core 2.1 application (netcoreapp2.1) using the following packages:
Microsoft.EntityFrameworkCore.Relational (2.1.1)
Microsoft.EntityFrameworkCore.SqlServer (2.1.1)
LinqKit.Microsoft.EntityFrameworkCore (1.1.15)
Any help much appreciated.

What you are missing in the examples is the fact that all Expression<Func<..>> are either parameters or local variables. While the line
where w.WalletItems.Any(hasItems().Compile())
is not actually calling the hasItems method, but emits a MethodCallExpression (i.e. Expression.Call) to it inside the query expression tree, which is different type of expression not recognized by LINQKit Expand implementation, thus leading to unsupported expression by EF Core.
The solution is to put the method call result into a local variable and use that variable inside the query expression tree:
var hasItemsExpr = hasItems();
var query =
from w in _context.Wallets.AsExpandable()
where w.WalletItems.Any(hasItemsExpr.Compile())
select w;

Related

Regex.IsMatch() in dynamic linq

how to invoke Regex.IsMatch() in IQueryable?
i found the same question but it doesnt work
Invoking Regex.IsMatch() inside a dynamic linq query
first i have
IQueryable Database = data.Verses.Select($"new {{ ID, {TextSearchType.ToString()} }}");
ive tried
var searchResult = Database.Where(Parse());
public static LambdaExpression Parse()
{
ParsingConfig.Default.CustomTypeProvider = new MyCustomTypeProvider();
var options = RegexOptions.IgnoreCase;
string compilableExpression = $"Regex.IsMatch({TextSearchType.ToString()}, \"(^| ){Keyword}($| )\", #0) == true";
ParameterExpression parameter = Expression.Parameter(typeof(Verses));
var DynamicExpression = DynamicExpressionParser.ParseLambda(new[] { parameter },
null,
compilableExpression,
options);
return DynamicExpression;
}
public class MyCustomTypeProvider : DefaultDynamicLinqCustomTypeProvider
{
public override HashSet<Type> GetCustomTypes()
{
return new HashSet<Type>
{
typeof(Object),
typeof(Boolean),
typeof(System.Text.RegularExpressions.Regex),
typeof(System.Text.RegularExpressions.RegexOptions),
};
}
}
TextSearchType is property name in Verses class.
im getting this error
No generic method 'Where' on type 'System.Linq.Queryable' is
compatible with the supplied type arguments and arguments. No type
arguments should be provided if the method is non-generic.
here is the linq code the im trying to convert to linq dynamic
var rx = new Regex("(^| )" + keyword + "($| )", RegexOptions.IgnoreCase);
var searchResult = Database.AsEnumerable()
.Where(x => rx.IsMatch(x.AyahText)).ToList();
You can not call IsMatch or any other Non-SQL supported functions (see the full list here) in LINQ to Entities.
In order to do what you want, you to have two possible options (maybe more, but I know two at this moment):
Get the items by raw filtering (without using IsMatch), converting the result to List, by calling .ToList() at the end of your query. And then you can filter the collection by Regex.IsMatch.
To be honest, this is not a greater solution, and I wouldn't say I like it.
Create a stored procedure and call it from the c# (official documentation here).

Building a parameterized EntityFramework Core Expression

Hi, I'm trying to build an Expression to get a generic entity by its primary key and getting a parameterized sql query.
Currently I can get the correct WHERE query, but it isn't parameterized.
public async Task<TDbo> Get(TKey key, Expression<Func<TEntity, TKey>> keySelector)
{
var propertyRef = keySelector.Body;
var parameter = keySelector.Parameters[0];
var constantRef = Expression.Constant(key);
var equals = Expression.Equal(propertyRef, constantRef);
var comparer = Expression.Lambda<Func<TEntity, bool>>(equals, parameter);
return await _context.Set<TDbo>().SingleOrDefaultAsync(comparer);
}
This results in the following query:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = 1\r\nLIMIT 2,
instead of the wanted:
SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = #__s_0\r\nLIMIT 2
It's because of Expression.Constant(key). Value constant expressions are not parameterized by the query translator. What you need is an expression referring to a property or field of another expression (which could be constant). That's basically what C# compiler emits for closures.
One way is to actually use the C# compiler to create lambda expression with closure and take the body:
Expression<Func<TKey>> keyValue = () => key;
var variableRef = key.Body;
(the variableRef is a replacement of yours constantRef)
Another way is to use anonymous, tuple or specific class type to create explicit closure instance and bind the corresponding property or field. For instance, with anonymous type:
var variableRef = Expression.Property(Expression.Constant(new { key }), "key");
or with System.Tuple:
var variableRef = Expression.Property(Expression.Constant(Tuple.Create(key)), "Item1");
The actual method doesn't really matter (I personally prefer the first variant with lambda) - all they will cause creating parameter by EF Core query translator.

c# FsCheck cannot convert lambda expression

I'm trying to get a C# FsCheck generator to generate a series
of commands which are initialized with random strings. I came up with
the following solution:
public Gen<Command<A,B>> Next(B value)
{
var gen1 = Arb.Default.String().Generator;
var gen2 = Gen.two(gen1);
var gen3 = gen2.select((Command<A,B>)(s => new DerivedCommand(s.Item1,s.Item2)))
//DerivedCommand extends Command<A,B>
return Gen.OneOf(gen3);
}
However, VS cannot build this code:
Cannot convert lambda expression to type Command<A,B> because it is not a delegate type
I have searched solutions for this error message, but nothing I found helped.
I am using System.Linq and System.Data.Entity. Any suggestions for resolving this issue are much appreciated.
You're trying to cast (s => new DerivedCommand(s.Item1,s.Item2)), which is a lambda expression, to (Command<A,B>), which (I assume) is a class.
You probably need something like:
var gen3 = gen2.select(s => (Command<A,B>)(new DerivedCommand(s.Item1,s.Item2)));

Does the LINQ Expression API offer no way to create a variable?

I want to validate my assumption that the LINQ Expression API does not have any means for us to create an expression that represents the creation of a local variable.
In other words, you cannot create an expression to represent:
int local;
since that is a variable declaration statement, and the API does not support statement lambdas. The only state that a lambda expression, as represented by the LINQ Expression API (and not a delegate instance) can work with is parameters it receives and the captured variables it receives via a closure.
Is my assumption (based on a few months of practice of the LINQ Expression API) correct?
False. There are some overloads of Expression.Block to do it.
What is true is that you can't create a lambda expression through the use of the C# compiler that has a variable, but that is a limitation of the compiler.
So you can't
Expression<Func<int>> exp = () => {
int v = 1;
return v;
};
but you can
var variable = Expression.Variable(typeof(int));
var lambda = Expression.Lambda<Func<int>>(
Expression.Block(
new[] { variable },
Expression.Assign(variable, Expression.Constant(1)),
variable)); // With lambda expressions, there is an implicit
// return of the last value "loaded" on the stack
since that is a variable declaration statement, and the API does not support statement lambdas.
This was true in .NET < 4.0 . In .NET 4.0 Microsoft added Expression methods to build nearly everything that can be present in the body of a method (there are some missing "things", like unsafe code keywords/operators, plus there are the primitives but there aren't complex constructs like the for or lock, that can be built on top of other constructs). Note that 90% of those added things are incompatible with LINQ-to-SQL/EF.
Well, you can use Expression.Block to declare a block which contains local variables...
For example:
using System;
using System.Linq.Expressions;
public class Test
{
static void Main()
{
var x = Expression.Variable(typeof(int), "x");
var assignment1 = Expression.Assign(x, Expression.Constant(1, typeof(int)));
var assignment2 = Expression.Assign(x, Expression.Constant(2, typeof(int)));
var block = Expression.Block(new[] { x }, new[] { assignment1, assignment2 });
}
}
That builds an expression tree equivalent to:
{
int x;
x = 1;
x = 2;
}
The C# compiler doesn't use this functionality within lambda expression conversions to expression trees, which are currently still restricted to expression lambdas, as far as I'm aware.

Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type

I am working with .NET4.5 and VS2013, I have this query that gets dynamic result from db.
dynamic topAgents = this._dataContext.Sql(
"select t.create_user_id as \"User\", sum(t.netamount) as \"Amount\" from transactiondetail t where t.update_date > sysdate -7 group by t.create_user_id")
.QueryMany<dynamic>();
Following statement fails with compilation error Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type
without even allowing me to run it
topAgents.ToList().Select(agent => new
{
User = agent.User != null ? string.Format("{0}", agent.User).Replace("CORPNTGB\\", "") : null,
Amount = agent.Amount
});
while this one with foreach works just fine.
var data = new List<List<object>>();
foreach (dynamic agent in topAgents)
{
data.Add(new List<object>
{
agent.User != null ? string.Format("{0}", agent.User).Replace("CORPNTGB\\", "") : null,
agent.Amount
});
}
In my eyes after I topAgents.ToList() they could be interpreted as equivalent, is it because I explicitly state that var data = new List<List<object>>(); that second statement is allowed by compiler?
Why doesn't compiler allow LINQ select, but allows for each`?
The problem is that topAgents is dynamic - so your ToList() call is dynamic, and so is Select. That has issues that:
you can't use lambda expressions for dynamic calls like this;
dynamic calls don't find extension methods anyway.
Fortunately, the operations don't need to be dynamic just because the element type is dynamic. You could use:
IEnumerable<dynamic> topAgents = ...;
... or just use var. Both of those should be fine.

Categories