Keep in mind that the inner Any method accepts a Func<MyChildEntity, bool> argument, not a Expression<Func<MyChildEntity, bool>>.
I think I'm aware of the difference between the 2 (function pointer vs expression tree). But still, the inner Any method accepts a func, not an expression, so..
..this works:
var entities = session.Query<MyEntity>()
.Where(m => m.Children.Any(s => s.Prop == "test"))
.ToListAsync();
This doesn't:
Func<MyEntityChild, bool> childFunc = m => m.Prop == "test";
var entities = session.Query<MyEntity>()
.Where(m => m.Children.Any(childFunc))
.ToListAsync();
This doesn't work either:
var entities = session.Query<MyEntity>()
.Where(m => m.Children.Any(s => childFunc(s)))
.ToListAsync();
At the moment I'm using NHibernate to process the IQueryable, but forget about NHibernate and possible NHibernate bugs..how is the first (working) example different from the other two (not working) from the point of view of IQueryable consumer? (as stated, NHibernate in this instance..)
edit: spelling
further edit: corrected comparison from = to ==
You cannot put Func and make query work, it should be Expression. To make your query translatable, expression should be preprocessed to make it work. It can be done by LINQKit
Activate extension:
builder
.UseSqlServer(connectionString)
.WithExpressionExpanding(); // enabling LINQKit extension
Then use Invoke LINQKit extension:
Expression<Func<MyEntityChild, bool>> childFunc = m => m.Prop == "test";
var entities = session.Query<MyEntity>()
.Where(m => m.Children.Any(s => childFunc.Invoke(s)))
.ToListAsync();
The only problem I could find is that you're missing an = in the function:
\\ '==' instead of '='
Func<MyEntityChild, bool> childFunc = m => m.Prop == "test";
Now this should work:
var entities = session.Query<MyEntity>()
.Where(m => m.Children.Any(s => childFunc(s)))
.ToListAsync();
Related
In my app I have some queries that use the same repeated logic:
var someThings = context.table1
.where(SomeLogic)
.ToList();
With EF Core 2.1 I could encapsulate this logic in a layer with all these expressions:
public static Expression<Func<MyObject, bool>> SomeLogic =>
myObject => myObject.CreationDate.Date == DateTime.Now.Date
&& (myObject.Whatever.HasValue || myObject.MoreWhatever);
Now I discovered this was being evaluated in memory, and that's bad.
If I do something like:
var someThings = context.table1
.where(myObject =>
myObject.CreationDate.Date == DateTime.Now.Date
&& (myObject.Whatever.HasValue || myObject.MoreWhatever))
.ToList();
then the query is evaluated in the DB, but I am putting some logic in the wrong layer.
I tried to subsitute Expression with a function or any other tool, but I don't find a way to do it.
Is there a way to encapsulate the logic of a query in a layer as I was doing before, but preserving EF rules so that this query can still be evaluated in the DB?
Thanks.
Why you need a "real" expression and not just a Lambda is explained in this answer. The created Expression can be created anywhere and passed as a parameter to the function that executes the query.
This answer should guide the way you need to go. You only have to replace the two dummy expressions with the whatever.hasvalue...stuff
var param = Expression.Parameter(typeof(MyObject), nameof(MyObject));
// myObject.CreationDate.Date == DateTime.Now.Date
Expression dateExpression = Expression.Equal(Expression.Constant(DateTime.Now),
Expression.PropertyOrField(param, "CreationDate"));
var dummyExpression1 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
var dummyExpression2 = Expression.Equal(Expression.Constant(1), Expression.Constant(1));
// && (myObject.Whatever.HasValue || myObject.MoreWhatever)
Expression orExpression = Expression.Or(dummyExpression1, dummyExpression2);
Expression allConditions = Expression.And(dateExpression, orExpression);
//myObject =>
Expression myExpression = Expression.Lambda<Func<MyObject, bool>>(allConditions, param);
var someThings = context.table1
.where(myExpression)
.ToList();
I had the most trouble with Expression.PropertyOrField. If you have nested structures you need to loop through the data structure and call Expression.PropertyOrField with the first parameter being the result from the previous call to Expression.PropertyOrField.
you can easily create dynamic queries in c# if you add more restrictions to the current query.
var list = new List<Item>();
var q = list.AsQueryable();
q = q.Where(x => x.Size == 3);
q = q.Where(x => x.Color == "blue");
In this case, every new predicate is added performing an AND operation with the previous. The previous result is equivalent to:
q = list.Where(x => x.Size == 3 && x.Color == "blue");
Is it possible to achieve the same result but with OR instead of AND?
q = list.Where(x => x.Size == 3 || x.Color == "blue");
The idea is to have a variable number of expressions that are joined with OR operator.
Expected result would need to be written in some how similar to the following pseudo code:
var conditions = new List<Func<Item, bool>>();
And later iterate conditions to build something like:
foreach(var condition in conditions)
finalExpression += finalExpression || condition;
Another possible solution to this, especially when someone doesn't want to use an external library is using expression trees.
Add a following expression extension:
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
As an example imagine you have a Container entity, which has InnerContainer nested entity with two properties Name and Id. Then you can use it in the following way:
Expression<Func<Container, bool>> whereQuery = c => c.InnerContainer.Name == "Name1";
whereQuery = whereQuery.Or(c => c.InnerContainer.Name == "Name2");
whereQuery = whereQuery.Or(c => c.InnerContainer.Id == 0);
var result = query
.Where(whereQuery)
.ToList();
Materializing such query will result in the following SQL:
SELECT [x].[Id], [x].[InnerContainerId]
FROM [Containers] AS [x]
LEFT JOIN [InnerContainer] AS [x.InnerContainer] ON [x].[InnerContainerId] = [x.InnerContainer].[Id]
WHERE [x.InnerContainer].[Name] IN (N'Name1', N'Name2') OR ([x].[InnerContainerId] = 0)
This way you can hold lambdas in a collection and loop through them.
Thanks to Raphaƫl Althaus that gave the following link:
http://www.albahari.com/nutshell/predicatebuilder.aspx
Predicate builder is the solution. You can use it installing LinqKit from Nuget. In that url you can find also the implementation of this class.
Note: in order to make this work with LinqToSql or LinqToEntities the IQueriable Object must be transformed using "AsExpandable()" method, for memory objects it's not required
I have a EF query in which i'm using lambda expressions, when i try to get the difference between two dates, it throws me exception
The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
my query is
var unApprovedLeaves = db.Leaves.Where(l => l.Status.Id == 1 && ((System.DateTime.Now.Date - l.ToDate.Date).TotalDays)==0)
.Include("Employee")
.Include("Employee.Manager")
.ToList();
can anyone tell me how do i get this thing right?
You must use SqlFunctions helper from System.Data.Objects.SqlClient. Try this:
var today = DateTime.Now.Date;
var unApprovedLeaves = db.Leaves.Where(l => l.Status.Id == 1 &&
(SqlFunctions.DateDiff("day", today, l.ToDate))==0)
.Include("Employee")
.Include("Employee.Manager")
.ToList();
In Entity Framework it is recomended to use DbFunctions helper from System.Data.Entity
Try this:
var today = DateTime.Now.Date;
var unApprovedLeaves = db.Leaves.Where(l => l.Status.Id == 1 &&
(DbFUnction.DiffDays(today, l.ToDate))==0)
.Include("Employee")
.Include("Employee.Manager")
.ToList();
I have a LINQ query where I want to select and read the p.Api value.
var api = DataAccessNew.Instance.dcServers.Where(p => p.Ip == IpAddress).Select(p => p.Api);
How do I read the p.Api value?
I have tried api.ToString() but I get SQL instead of actual column value.
You are getting an IEnumerable<> back (and your ToString call is showing you the value of that expression).
If you are expecting a single value, do this:
var api = DataAccessNew.Instance.dcServers
.Where(p => p.Ip == IpAddress)
.Select(p => p.Api)
.Single();
You might be interested to read about the other methods like Single(): SingleOrDefault, First, FirstOrDefault. Which one you used depends on whether you are expecting a single or multiple values returned (Single vs. First) and what you want to happen if there are no values (the *Default methods will return the type default instead of throwing an exception).
Or if you want to look at all the returned values:
var api = DataAccessNew.Instance.dcServers
.Where(p => p.Ip == IpAddress)
.Select(p => p.Api);
foreach (var apiValue in api)
{
// apiValue will have the value you're looking for.
}
Try this snippet of code:
string apiValue = api.FirstOrDefault().ToString();
your syntex seems ok..
By the way try this
string api =DataAccessNew.Instance.dcServers.Where(p => p.Ip == IpAddress).Select(p => p.Api).FirstOrDefault();
if p.Ip is a unique key in your table you could try to add .FirstOrDefault() after your Linq query.
public string getselectedvalue(ListBox l)
{
string vtext="",vval="";
var selectedQueryText = l.Items.Cast<ListItem>().Where(item => item.Selected);
var selectedQueryVal = l.Items.Cast<ListItem>().Where(item => item.Selected).Select(item => item.Value);
vtext= String.Join("','", selectedQueryText ).TrimEnd();
vval= String.Join("','", selectedQueryVal ).TrimEnd();
return v;
}
Linq and EF4.
I have this Linq query in query syntax I would like convert into query method.
Are you able to do it? I tried more tha 2 hours without success :-(
Thanks for your time
CmsContent myContentObj = (from cnt in context.CmsContents
from categoy in cnt.CmsCategories
where categoy.CategoryId == myCurrentCategoryId && cnt.ContentId == myCurrentContentId
select cnt).Single();
My original answer selected the wrong item. It's a bit more complicated than what I had (which Ani has posted). Here's what I believe is an equivalent query however and should perform better:
CmsContent myContentObj =
context.CmsContents
.Where(cnt => cnt.ContentId == myCurrentId
&& cnt.CmsCategories
.Any(categoy => categoy.CategoryId == myCurrentCategoryId))
.Single();
Here is a non-direct translation that I believe performs the same task in much less code:
var myContentObj = context.CmsContents.Single(
x => x.ContentId == myCurrentContentId &&
x.CmsCategories.Any(y => y.CategoryId == myCurrentCategoryId)
);
Here's how the C# compiler actually does it, with some help from .NET Reflector to verify:
var myContentObj = context
.CmsContents
.SelectMany(cnt => cnt.CmsCategories,
(cnt, categoy) => new { cnt, categoy })
.Where(a => a.categoy.CategoryId == myCurrentCategoryId
&& a.cnt.ContentId == myCurrentContentId)
.Select(a => a.cnt)
.Single();
Essentially, the 'nested' from clauses results in a SelectMany call with a transparent identifier (an anonymous-type instance holding the 'parent' cnt and the 'child' categoy). The Where filter is applied on the anonymous-type instance, and then we do another Select projection to get back the 'parent'. The Single call was always 'outside' the query expression of course, so it should be obvious how that fits in.
For more information, I suggest reading Jon Skeet's article How query expressions work.