I have a Where clause predicate which is fetching records .Now if I use the same in a where clause as a Predicate(ie Func(x,bool) ) it returns no records.My impression was that both were equivalent. - see below
var nonPredicate=this.ObjectSet.Where(x=>!String.IsNullOrEmpty(x.Email) && x.Email.Contains("AND")).ToList();
Func<Model,bool) clause=x=> x=>!String.IsNullOrEmpty(x.Email) && x.Email.Contains("AND");
var predicateRes=this.ObjectSet.Where(clause).ToList();
I expected same results in both cases -but the first produces 29 record result and the second 0 records.
Any Help would be great
Thanks
Linq to Entitites uses Expression<Func<?,?>> for it's query methods. Lambda expressions are automatically coersed to an expression (Expression<Func<?,?>>) or delegate (Func<?,?>) depending on the type of the variable or argument it is stored in.
// Inline expression
var nonPredicate = this.ObjectSet.Where(x =>
!string.IsNullOrEmpty(x.Email) && x.Email.Contains("AND")).ToList();
// Delegate type
Func<Model,bool> clauseDelegate = x =>
!string.IsNullOrEmpty(x.Email) && x.Email.Contains("AND");
// Expression type
Expression<Func<Model,bool>> clauseExpr = x =>
!string.IsNullOrEmpty(x.Email) && x.Email.Contains("AND");
var predicateRes = this.ObjectSet.Where(clauseExpr).ToList();
Expressions are an object representation of the lambda, which allows the library to translate it into SQL for communication with the database. The same is not possible with delegates, since the structure is already compiled into IL or machine code.
Related
How to have the select query projection to a lambda expression in Entity framework..
In repo class
database.Employees.Where(s => s.EmpID == curEmp.ID)
.Select(s => new EmployeeModel
{
Value = s.Name,
Text = s.ID
// I have 40 fields and 10 join table projections here so much code
});
Since I have many fields to project, so created a separate class with the lambda expressions for those long select projections.
public class ProjectionQueries
{
public static readony Fun<Employee, EmployeeModel> GetEmployeeAsModel = (emp)
=> new EmployeeModel
{
Value = s.Name,
Text = s.ID...
...
..
..
Address = new AddressModel{.....},
Country = new CountryModel{.....}
};
}
Then called the above lambda expression from the select clause of the repository method.
database.Employees.Where(s => s.EmpID == curEmp.ID)
.Select(x => ProjectionQueries.GetEmployeeAsModel(x));
This way it is well segregated the code. But when I do this I get an error saying
The client projection contains a reference to a constant expression of the System.Func<*,*>. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead.
Can't use this way to make my code less in the repository class. ?
You could separate this out as a static extension method.
public static class EmployeeExtensions{
public static IQueryable<EmployeeModel> AsEmployeeModels(this IQueryable<Employee> emps){
return emps.Select(emp=> new EmployeeModel
{
Value = emp.Name,
Text = emp.ID...
});
}
}
and use it as:
database.Employees.Where(s => s.EmpID == curEmp.ID).AsEmployeeModels();
The #gokul-panigrahi answer show a solution with extension method, that I advice to use because I found the syntax smoothest.
The error is :
The client projection contains a reference to a constant expression of the System.Func<,>. This could potentially cause a memory leak; consider assigning this constant to a local variable and using the variable in the query instead.
The error indicate EF Core can't translate System.Func<*,*> to SGBD language. EF Core client evaluation. A solution is to execute the untranslatable part in client :
database.Employees
// 1) Prepare Query
.Where(s => s.EmpID == curEmp.ID)
// 2) Execute query on server
.AsEnumerable()
// 3) Next is execute in client
.Select(x => ProjectionQueries.GetEmployeeAsModel(x));
See the ms-doc for more information about Client vs Server Evaluation
But client evaluation could result in poor performance. In your case, if the projection can limit the number of column read in database (and limit the data transmit on network).
A other solution, it is to modify the type of GetEmployeeAsModel :
public static readony Expression<Fun<Employee, EmployeeModel>> GetEmployeeAsModel = ...
Thus EF Core can try translate the expression to SGBD's language. Try because the expression can use some term untranslatable like Func<*,*>.
What is the difference between Func and Expression<Func>?
A Func is delegate (trivially a pointer to a zone code in memory). EF Core don't know how translate a delegate.
A Expression is a specific structure of instruction, that is possible to explore :
Expression<Func<int,int>> expression = (x) => x % 2;
// Explore the expression
var operation = expression.Body as BinaryExpression;
var leftPart = operation.Left as ParameterExpression;
var rightPart = operation.Right as ConstantExpression;
// Translate
Console.WriteLine("#" + leftPart.Name);
// #x
Console.WriteLine(operation.NodeType);
// modulo
Console.WriteLine(rightPart.Value);
// 2
EF Core do this to translate the expression to SGBD's language, but sometime the expression contains a term unknown by EF Core. Then this produce a error like above.
I have code like this:
foreach (DataRow row in tmpDatosModulos.Rows)
{
tmpBSCID += row["ModuloURL"].ToString();
tmpBSCID = tmpBSCID.Replace("../BSC/wf_BSC_Reporte.aspx?BSCID=", "");
}
bsc = _c.ConfiguracionesBalance.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A") && x.ID == int.Parse(tmpBSCID)).ToList();
I get error when I debbug it:
LINQ to Entities does not recognize the method 'Int32
Parse(System.String)' method, and this method cannot be translated
into a store expression.
I read another questions about that, and solution explained is to parse outside LINQ expression so I do something like this:
foreach (DataRow row in tmpDatosModulos.Rows)
{
tmpBSCID += row["ModuloURL"].ToString();
tmpBSCID = tmpBSCID.Replace("../BSC/wf_BSC_Reporte.aspx?BSCID=", "");
}
tmpBSCID = int.Parse(tmpBSCID);
bsc = _c.ConfiguracionesBalance.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A") && x.ID == tmpBSCID).ToList();
but I get
Cannot implicity convert int to string
in this line tmpBSCID = int.Parse(tmpBSCID);
It's a little confussing because I'm converting string to int and no viceversa. Regards
What kind of type is your variable "tmpBSCID"? From the code you poste seems it's a string, and in the part
tmpBSCID = int.Parse(tmpBSCID);
you are assigning a int field to a string variable. If you need to keep using tmpBSCID, after the parsing method use the "ToString()", otherwise you can create a new int variable (or var).
I would rewrite it as
var tmpBSCIDValue = int.Parse(tmpBSCID);
bsc = _c.ConfiguracionesBalance.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A") && x.ID == tmpBSCIDValue).ToList();
You need to assign a new variable (of type integer) to the result of your parsed string, and applying the integral variable to your LINQ query, i.e.:
var myIntegerBSCID = int.Parse(tmpBSCID);
bsc = _c.ConfiguracionesBalance
.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A") && x.ID == myIntegerBSCID)
.ToList();
Other than dynamic, C# will not allow you to change the type of an existing variable, i.e.
tmpBSCID = int.Parse(tmpBSCID);
can't work, because the LHS type would need to be Integral, whereas the original RHS type of tmpBSCID is of course string.
As an aside, consider using a StringBuilder to build up tmpBSCID rather than looping and concatenating strings.
And finally, you might also consider the case where tmpBSCID cannot be parsed as an integer. You can use int.TryParse(out var myIntegerBSCID) as an alternative - it returns false if the parse fails.
From your code (+=) I conclude what you're trying to do is: collect a list of ID values from a data table, not just one single ID. So you have to build this list and then use Contains in the LINQ query:
var idValues = tmpDatosModulos.Rows.Select(row =>
{
tmpBSCID = row["ModuloURL"].ToString()
.Replace("../BSC/wf_BSC_Reporte.aspx?BSCID=", "");
return int.Parse(tmpBSCID);
}).ToList();
bsc = _c.ConfiguracionesBalance
.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A")
&& idValues.Contains(x.ID)).ToList();
Side note: you seem to be confident that int.Parse always succeeds. I'd prefer using int.TryParse.
Althoug LINQ does not recognize int.Parse() you can do bsc = _c.ConfiguracionesBalance.Where(x => x.mdEstatusRegistro && x.sEstatus.Equals("A") && x.ID == (int)(tmpBSCID)).ToList(); to force the conversion.
Looking at your first error, I gather that _c.ConfiguracionesBalance is an IQueryable.
IQueryables are different that IEnumerables in the fact that an IQueryable doesn't hold the code to create an Enumerator for your sequence. It holds an Expression and a Provider. The Provider knows who should perform the Expression (usually an external process like a database). The provide also knows the language this external process speaks (for example SQL). The provider knows how to translate the Expression into this language.
As soon as you want to access the first element of the sequence represented by your IQueryable, the IEnumerator for this sequence is fetched from the IQueryable. The expression is sent to the Provider who translates it into the language for the other process and orders this other process to perform the query.
This provider can't translate your own functions into SQL. In fact, although there are a lot of .NET functions that can be translated, there is a list of supported and not supported LINQ functions
In this list you can see that Int32.Parse is not supported. Therefore it was wise to parse locally.
You don't write the type of tmpBSCID, but from your statements I gather that it is a string. If you'd written the type, you'd already answered the questioin for yourself:
string tmpBSCID += row["ModuloURL"].ToString(); // from your code
tmpBSCID = Int32.Parse(tmpBSCID);
And you wonder why the compiler complains that it can't convert the int to a string?
EDIT: I managed to reduce my code to the least-common-denominator with the assistance of LinqPad.
I have a method that returns a complex LinqKit predicate. The desired result is to be able to reuse this method whenever I need to perform this query. It works fine in a Where clause against an IQueryable collection of these entities, but I can't for the life of me figure out how to use this predicate when I have this entity as a single property of an object, and want to use predicates to query the other properties of that entity.
I'll give you a simplified example of what I mean.
// Note: .Includes removed for brevity's sake.
var task = Tasks.First(t => t.QaConfigTaskId == 2);
var predicate = PredicateBuilder.True<Task>();
// Ensure that the task's own properties match.
predicate = predicate.And(t => t.TaskType == task.TaskType &&
t.Description == task.Description &&
t.PreTreatment == task.PreTreatment &&
t.Treatment == task.Treatment);
var structureAnalysis = task.StructureAnalysis;
var query = PredicateBuilder.True<StructureAnalysis>();
query = query.And(analysis =>
// The names match
analysis.Name == structureAnalysis.Name &&
// We have the same # of goals so they must all match.
analysis.Goals.Count == structureAnalysis.Goals.Count
);
predicate = predicate.And(t => query.Invoke(t.StructureAnalysis));
This will work fine:
StructureAnalyses.AsExpandable().Where(query).Dump();
...assuming StructureAnalyses is an IQueryable of my StructureAnalysis objects from Entity Framework.
But let's say I want to get the Tasks, filtering with the related StructureAnalyses. How can I do such a thing? Keep in mind that in reality, query is built out by a reusable function, and predicate is built out in a separate function that calls it, so I can't simply merge the two queries.
It compiles but fails when I try and execute the query:
Tasks.AsExpandable().Where(predicate).Dump();
...with "The parameter 't' was not bound in the specified LINQ to Entities query expression.", or the like. In this example, Tasks is an IQueryable that contains all of the Task type entities in my DB.
I've also tried altering this line:
predicate = predicate.And(t => query.Invoke(t.StructureAnalysis));
...to:
compiled = query.Compile();
predicate = predicate.And(t => compiled(t.StructureAnalysis));
That also compiles but fails with, "Unable to cast object of type 'System.Linq.Expressions.FieldExpression' to type 'System.Linq.Expressions.LambdaExpression'." and understandably so.
I've also tried calling Expand on both query and compiled, which had no effect, as well as the following "solutions":
This one seems incomplete (Utility is undefined):
https://stackoverflow.com/questions/26105570/linqkit-the-parameter-was-not-bound-in-the-specified-linq-to-entities-query-e
This one simply says, "it can't be done" which I refuse to believe:
Entity Framework Code First 4.3 / LINQKit predicate for related table
I ended up finding the solution on my own. The trick is to wrap the invocation of query in another predicate that accesses the property, then expand that predicate when calling it. So for my example where I'm attempting to invoke a query on a single StructureAnalysis, my code now looks like this:
// Note: .Includes removed for brevity's sake.
var task = Tasks.First(t => t.QaConfigTaskId == 2);
var predicate = PredicateBuilder.True<Task>();
// Ensure that the task's own properties match.
predicate = predicate.And(t => t.TaskType == task.TaskType &&
t.Description == task.Description &&
t.PreTreatment == task.PreTreatment &&
t.Treatment == task.Treatment);
var structureAnalysis = task.StructureAnalysis;
var query = PredicateBuilder.True<StructureAnalysis>();
query = query.And(analysis =>
// The names match
analysis.Name == structureAnalysis.Name &&
// We have the same # of goals so they must all match.
analysis.Goals.Count == structureAnalysis.Goals.Count
);
//// HERE'S WHAT'S NEW ////
Expression<Func<Task, bool>> subPredicate = t => query.Invoke(t.StructureAnalysis);
predicate = predicate.And(subPredicate.Expand());
Tasks.AsExpandable().Where(predicate).Dump();
This is not another question about 'How Can I Sort Dynamically (based on an arbitrary user provided field)?'
The question is -- how can I change sort order when I know the potential sorts in advance? (And thus avoid reflection / custom Expression building typically associated with truly dynamic sorting.)
Take for instance this subquery (shortened for this example) of a larger query:
(from solutionIds in context.csExtendedQAIncident_Docs
where solutionIds.tiRecordStatus == 1
&& (from solutionProductAssocation in context.csProductDocs
where solutionProductAssocation.iSiteId == Settings.Current.WebUtility().Onyx.SiteId
&& (from allowedProduct in context.KB_User_Allowed_Products
where allowedProduct.UserId == userId
select allowedProduct.ModelCode
).Contains(solutionProductAssocation.chModelCd)
select solutionProductAssocation.chIdNo).Distinct().Contains(solutionIds.chIdNo)
).OrderByDescending(s => s.dtUpdateDate)
.Select(s => s.chIdNo)
.Take(count ?? Settings.Current.WCFServices().Output.HomePage.MaxRows)
The OrderByDescending portion works as I would expect.
Now -- I want to factor that out like the following:
Expression<Func<csExtendedQAIncident_Doc, IComparable>> ordering = (s) => s.dtUpdateDate;
if (viewType == HomepageViewType.MostViewed)
ordering = (s) => s.vchUserField8;
else if (viewType == HomepageViewType.MostEffective)
ordering = (s) => s.vchUserField4;
and then use:
OrderByDescending(ordering)
This does compile, but blows up at run-time.
Unsupported overload used for query operator 'OrderByDescending'.
This of course comes from deep in the bowels of System.Data.Linq.SqlClient.QueryConverter -- in particular VisitSequenceOperatorCall. Reflectoring that code reveals that the following conditions must be met for OrderByDescending to properly evaluate. 'mc' is the MethodCallExpression passed into the method.
if (((mc.Arguments.Count != 2) || !this.IsLambda(mc.Arguments[1]))
|| (this.GetLambda(mc.Arguments[1]).Parameters.Count != 1))
{
break;
}
So essentially that MethodCallExpression has to have 2 arguments, the second of which has to be a Expressions.LambdaExpression with a single parameter (presumably the sort field). If that code breaks out, the exception that I got is thrown.
So clearly I have not constructed the expression correctly. Without digging in any further here, does anyone know how to correctly construct the sorting Expression?
I think the unsupported part of your code is the use of IComparable as a general return type for your ordering expression. If you consider the plain use of OrderByDescending, the compiler-generated lambda expression has a return type of the type of the property that you're ordering by: for example, an Expression<Func<csExtendedQAIncident_doc, string>> for a string property.
One possible answer, although I'm not sure whether it works in your case, is to first create an unordered query:
IQueryable<Foo> unorderedQuery = from f in db.Foo select f;
And then, depending on the sort:
IOrderedQueryable<Foo> orderedQuery = unorderedQuery
.OrderBy(f => f.DefaultSortKey);
if (sortBy == SortByName)
orderedQuery = unorderedQuery.OrderBy(f => f.Name);
else if (sortBy == SortByDate)
orderedQuery = unorderedQuery.OrderBy(f => f.Date);
// etc.
I believe that this will not work unless the two possible fields have the identical type.
Then the linq to sql will (if possible) correctly create the relevant sql.
so for example if both of those fields were DateTimes:
Expression<Func<csExtendedQAIncident_Doc, DateTime>> ordering =
s => s.dtUpdateDate;
if (viewType == HomepageViewType.MostViewed)
ordering = (s) => s.vchUserField8; // a DateTime
else if (viewType == HomepageViewType.MostEffective)
ordering = (s) => s.vchUserField4; // another DateTime
Then this would work just fine (I tested it and it worked)
You could instead do a per type order by either a series of nested switch/if statements of by constructing a dictionary or similar structure to get them.
For the linq to sql to work without explicit dynamic creation of the query I believe it must know the precise type of the query as opposed to just it being an IComparable...
Let's say we need to apply several conditions to select from a table called "Things" (unknown count and nature)
if conditions are known, we can write
db.Things.Where(t=>foo1 && foo2 || foo3);
but if we have to build that Where condition programatically, I can imagine how can we apply ANDed conditions
IQuerable DesiredThings = db.Things.AsQuerable();
foreach (Condition c in AndedConditions)
DesiredThings = DesiredThings.Where(t => GenerateCondition(c,t));
What about ORed conditions ?
Note: we don't want to perform union, unique, or any other costly operations, it's desired that a query is generated as if we write it ad-hock
Thanks in advance.
Addition:
PredicateBuilder: Dynamically Composing Expression Predicates
You could use the Expression class with static methods to do it run time.
The below code is ment to create a delegate taking one argument called value of type int
. It reads from buttom to top so the line in question is:
var method = LambdaExpression.Lambda(orExp, Expression.Parameter(typeof(int), "value"));
the body of the method compares the value of the parameter to a call to method Bar of a newly created object of type foo
var exp2 = Expression.Equal(Expression.Parameter(typeof(int), "value"), Expression.Property(Expression.New(typeof(Foo).GetConstructor(new Type[] { })), "Bar"));
It then creates a similar expression and or's them
var orExp = Expression.OrElse(exp1, exp2);
final thing is the call to compile. That call generates a delegate that can be used in your where method call.
hope it helps tho Im not 100% sure on the expression to get the value from a parameter
var exp1 = Expression.Equal(Expression.Parameter(typeof(int),"value"), Expression.Property(Expression.New(typeof(Bar).GetConstructor(new Type[] { })), "Foo"));
var exp2 = Expression.Equal(Expression.Parameter(typeof(int), "value"), Expression.Property(Expression.New(typeof(Foo).GetConstructor(new Type[] { })), "Bar"));
var orExp = Expression.OrElse(exp1, exp2);
var method = LambdaExpression.Lambda(orExp, Expression.Parameter(typeof(int), "value"));
method.Compile();
You might wanna look at invoke for invokation instead of compiling the expression, if you need the LambdaExpression to be translated into something different than binary code (E.g. into an SQL statement)
For OR, you have two choices:
use Union/Concat
write the Expression in code
The second is closer to the .Where(x => {a} || {b}).
If you are using LINQ-to-SQL, you can use Expression.Invoke to combine multiple separate lambda expressions (see this answer) - however, this isn't supported in Entity Framework. In EF, you have to build the entire expression as a single block, using Expression.OrElse; for example here or here.