Why does EntityFramework NOT use my predicate? - linq

I have a PredicateBuilder which I got it from
http://www.albahari.com/nutshell/predicatebuilder.aspx
And here is how I use it:
var Expression = PredicateBuilder.True<UCM_UserAgent>();
Expression = Expression.And(item => item.AgentText == "TestUserAgent Value");
Func<UCM_UserAgent, bool> SearchCriteria = Expression.Compile();
var Data = Context.UCM_UserAgent
.Where(SearchCriteria)
.ToList();
And when I checked SQL Profiler on my database:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[AgentText] AS [AgentText],
FROM [dbo].[UCM_UserAgent] AS [Extent1]
Now, where is the condition I added? I could not solve this. Unfortunately, this Entity Framework and Predicate Builder - Predicates being Ignored in SQL Query did not help either.
Edit:
To eliminate any misunderstandings; I do not want to use as x => x.AgentText = "SomeText". Because I want to build a dynamic LINQ query, thus I use PredicateBuilder.

If you want the predicate to be hadled by LINQ to Entities you need to pass the Expression<Func<T,bool>> version to the Queryable.Where() method instead of passing the Func<T,bool> version to the Enumerable.Where() method as you are currently doing, i.e. do not compile the expression into IL before calling the Where method or you will necessarily get the LINQ to Objects version.

Related

Entity Framework Core GroupBy Date

In C# you can group by .Date:
db.History.GroupBy(x => x.Timestamp.Date)
.Select(g => new { key = g.Key, aggregate = g.Count() })
However, the equivalent F# does not work:
db.History.GroupBy(fun x -> x.Timestamp.Date)
.Select(fun g -> { Date = g.Key; Count = g.Count()} )
The relevant record:
type DateCount = {
Date: DateTime
Count: int
}
It throws the following error:
System.InvalidOperationException: The LINQ expression 'DbSet<HistoryEntity>
.GroupBy(
source: h => copyOfStruct => copyOfStruct.Date.Invoke(h.Timestamp),
keySelector: h => h)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
How can I group by date?
So in C#, when you use LINQ-to-SQL queries, you are using the extension methods on IQueryable<T>. If we look at the signature of say, the GroupBy method, you will see that the function signature is actually
IQueryable<TSource>.GroupBy<TSource,TKey>(Expression<Func<TSource,TKey>> keySelector)
What's going on? Expression<> is a special type - when the C# compiler spots an Expression<> type, the compiler builds an AST, and passes the AST object (of type Expression<Func<>>), instead of the usual delegate. The underlying functions are expected to inspect the AST and build whatever query expression is finally needed, like SQL for querying the database.
You can try it yourself:
Expression<Func<int>> getRandom = () => 4; //random, chosen by fair dice roll
You can inspect the properties of getRandom to see the AST.
Since the magic happens in the C# compiler, when you do it in F#, this won't cut it.
To go into more detail, the F# compiler can recognize the Expression<>, but it does so by applying an implicit F# quotation - so you get an F# quotation wrapped method call that translates to the C# expression tree. (Sorry if, that was run on.)
F# has its own query comprehension builder for SQL. It lets you write the computation expressions like that of seq which translate to SQL queries. This works like you'd expect.
query {
for record in db do
select record
}
Group by .Date works when using in a query expression.
query {
for h in db.History do
groupValBy h h.Timestamp.Date into g
select {
Date = g.Key
Count = g.Count()
}
}
Code stolen from here.
If someone could explain why the query expression works but the LINQ version doesn't, I'd appreciate it :)

LinqKit PredicateBuilder adding to linq query

I have a linq query which I want to add some additional, optional WHERE conditions to using LinqKit Predicate Builder. However, I an struggling to get the additional predicate to work
This is my initial query:
var query = (from OP in ctx.OrganisationProducts
where OP.OrganisationID == orgID
orderby OP.Product.Name, OP.Product.VersionName
select OP).Include("Product");
As you can see, there is JOIN in there.
I then wish to add additional predicates if required:
if(!includeDisabledP42Admin || !includeDisabledOrgAdmin)
{
var pred = PredicateBuilder.True<OrganisationProduct>();
if (!includeDisabledP42Admin)
pred.And(op => op.Enabled);
if (!includeDisabledOrgAdmin)
pred.And(op => op.AccessLevel == "NA" || op.AccessLevel == "NU");
query = query.Where(pred);
}
However, the generated SQL is unchanged and the query returns the same number of rows.
I thought I might have had to do the Expand conversion as so:
query = query.AsExpandable().Where(pred);
But this causes a runtime error.
I think the issue is the fact that I am adding the predicate to a query that is already no longer a pure OrganisationProduct object, however I would like advise on how I insert my predicate at the right place.
Thanks and all :-)
You have to assign the return value of And to the predicate:
pred = pred.And(op => op.Enabled);
Side note: you may like this predicate builder that works without Expand/AsExpandable, but has the same syntax.

Linq in conjunction with custom generic extension method build error - An expression tree may not contain an assignment operator?

I've made a generic extension method that executes an action on an object and returns the object after that:
public static T Apply<T>(this T subject, Action<T> action)
{
action(subject);
return subject;
}
I'm unable to use this extension method in an EntityFramework Linq query because of:
An expression tree may not contain an assignment operator
Why is this?
The Linq query:
var parents = from p in context.Parent
join phr in context.Child on p.key equals phr.parentkey
into pr
select p.Apply(
x => x.Children = //The assignment operator that fails to build...
pr.ToDictionary(y => y.childkey, y => y.childname));
Well, assignment operator aside, how would you expect your Apply method to be translated into SQL? Entity Framework doesn't know anything about it, and can't delve into opaque delegates, either.
I suspect what you really need to do is separate out the bits to do in the database from the bits to do locally:
var dbQuery = from p in context.Parent
join phr in context.Child on p.key equals phr.parentkey into pr
select new { p, phr };
var localQuery = dbQuery.AsEnumerable()
.Select(pair => /* whatever */);

Nhibernate linq. The where extension method does not add the where clause to the SQL command, why?

I want to add the where clause to a linq statement, but it doesn't behave as i would expected it to.
When i use this code:
IQueryable<Employee> EmpQuery = from e in Session.Query<Employee>() where e.Surname == "Test" select e;
EmpQuery.ToList();
or i use this code:
IQueryable<Employee> EmpQuery = (from e in Session.Query<Employee>() select e).Where(e => e.Surname == "Test");
EmpQuery.ToList();
The where clause is included in the SQL command, but when i try it this way:
IQueryable<Employee> EmpQuery = from e in Session.Query<Employee>() select e;
EmpQuery.Where(e => e.Surname == "Test");
The where clause is not included in the SQL command. Why is this? Is there another way to dynamically add criteria to a Nhibernate Linq query?
You're not using the return value of Where. LINQ is designed around functional concepts - calling Where doesn't modify the existing query, it returns a new query which applies the filter. The existing query remains as it was - which means you can reuse it for (say) a different filter.
Note that your current query expression (from x in y select x, effectively) is pretty pointless. I would suggest simply writing:
var query = Session.Query<Employee>().Where(e => e.Surname == "Test");
Just to clarify on Jon's remark, your implementation would be fine with the following tweak:
IQueryable<Employee> modifiedQuery = EmpQuery.Where(e => e.Surname == "Test");
Then just invoke the appropriate enumerator (ToList, ToArray, foreach) on modifiedQuery. And I wouldn't say that it create a complete new query, but instead creates a query which wraps around the original (kind of along the lines of the adapter pattern). Granted, your example doesn't need the additions, but this is how you would add additional criteria onto an existing LINQ expression, and that is what your question actually asked.

Linq expression subsonic 3.0.0.3

I want to 'build' a combined query for Subsonic 3.0.0.3, what is the best way for this?
I tried;
Expression<Func<Person, bool>> exp = p => true;
Expression<Func<Person, bool>> fContinent = p => p.ContinentID == 1;
Expression<Func<Person, bool>> fType = p => p.TypeID == 1;
exp = Expression.Lambda<Func<Person, bool>>(Expression.AndAlso(exp, fContinent), exp.Parameters);
exp = Expression.Lambda<Func<Person, bool>>(Expression.AndAlso(exp, fType), exp.Parameters);
var personList = Person.Find(exp);
But that will give the exception "The binary operator AndAlso is not defined ..."
I also tried using predicates but that will throw exceptions as well (Expression.Invoke is not supported).
In subsonic 2 I would have used the SqlQuery object, but I would like to know the proper way to do this in version 3 using linq / expressions.
Have you tried And instead of AndAlso?
The right way to do this is to combine the lambda expression bodies, like this:
exp = Expression.Lambda<Func<Person, bool>>(
Expression.And(exp.Body, fContinent.Body), exp.Parameters);
Even if And is supported by your query provider, you'll also need to replace the parameter references in fContinent's Body with references to the parameter defined in exp--as is, your two expression bodies (combined with And) reference two distinct parameters, each named p.
See my answer to this question for the cleanest method to replace expression parameters.
I asked this question, but I am using the combined query in subsonic just like you.
In short, you want to use a PredicateBuilder to build up the query. When you want to execute it in your subsonic object (assuming ActiveRecord), use code like this:
var predicate = /* Build up predicate with PredicateBuilder */;
var recs = SubsonicClass.All().Where(predicate.Compile()).ToList();

Resources