Newbie LINQ Expressions question-
Expression<Func<TLookupModel, TValue>> idSelector;
IEnumerable<TLookupModel> source;
TValue id;
I'm trying to do (pseudo-code):
source.AsQueryable().FirstOrDefault(x => idSelector == id)
My feeble attempt thus far is along the lines of:
var expressionParam = idSelector.Parameters.First();
ConstantExpression selectedValueConstant = Expression.Constant(id, typeof(TValue));
var idEqualExpression = Expression.Equal(idSelector, selectedValueConstant);
var lambda = Expression.Lambda<Func<TLookupModel, bool>>(idEqualExpression, expressionParam);
var selectedSourceItem = source.AsQueryable().FirstOrDefault(lambda);
I think that gives you a guess as to how I've been thinking so far. I've tried with and without the parameters, different combinations of Expression method calls, trying to get the "parameter" to come from the FirstOrDefault() call, but after reading lots of tutorials I can't get my head around how to extend a "member" expression to equal a constant in this way.
You got really close.
Your idExpression is an Expression in the form of x => x.Property. However, you're passing the whole expression to the Equal expression. Change that to pass only the body:
var idEqualExpression = Expression.Equal(idSelector.Body, selectedValueConstant);
Then you can compile the lambda and pass it to FirstOrDefault without casting to a queryable:
var selectedSourceItem = source.FirstOrDefault(lambda.Compile());
Related
I try to execute the following statement:
int count = this.objectReportsRepository.All()
.Count(or => (int)or.GetPropertyValue("ReportingUserId") == reportModel.ReportingUserId
&& or.GetPropertyValue("Reported" + target + "Id") == reportModel.GetPropertyValue("Reported" + target + "Id")
&& DbFunctions.TruncateTime((DateTime)or.GetPropertyValue("ReportDate")) == serverTimeToday);
However, I get an error:
System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Object GetPropertyValue[QuestionReport](InterpretumDAL.QuestionReport, System.String)' method, and this method cannot be translated into a store expression.
GetPropertyValue is actually an extension method that I wrote on my own to use reflection:
public static object GetPropertyValue<T>(this T sourceObject, string propertyName)
{
return sourceObject.GetType().GetProperty(propertyName).GetValue(sourceObject, null);
}
I want to execute the LINQ to Entities statement written on the top of my question, because I have different Report entities. For example, I have QuestionReport, UserReport, TagReport, etc. entities. I do all the same with them, but they have a different meaning. The QuestionReport entity stores reports for Question entities. The UserReport - for User entities and so on. So, instead of writing the same code n times, I want to user Reflection.
The only thing I could think of is to add a ToList() method call after the All() method call, but by doing this I actually load all entities in memory and only after this I Count the thing I want to count, instead of counting it with a simple query.
Help? Anyone? :)
You need to build expression tree in order to use it against linq to entities, try this expression.
var or = Expression.Parameter(typeof(ObjectReport));
var cond1 = Expression.Equal(
Expression.Property(or, "ReportingUserId"),
Expression.Constant(reportModel.ReportingUserId));
var cond2 = Expression.Equal(
Expression.Property(or, "Reported" + target + "Id"),
Expression.Constant(reportModel.GetPropertyValue("Reported" + target + "Id")));
var cond3 = Expression.Equal(
Expression.Call(
typeof(DbFunctions),
"TruncateTime",
Type.EmptyTypes,
Expression.Convert(Expression.Property(or, "ReportDate"), typeof(DateTime?))),
Expression.Convert(Expression.Constant(serverTimeToday), typeof(DateTime?)));
var cond = Expression.AndAlso(Expression.AndAlso(cond1, cond2), cond3);
var predicate = Expression.Lambda<Func<ObjectReport, bool>>(cond, or);
int count = this.objectReportsRepository.All().Count(predicate);
No offense man, but why even use entity framework if you're going to misuse generics like this?
Your repository should be a Repository<T> so that when you perform .All() you can just directly select the property value that you need via Func. Then in your code, wherever you need a Repository<QuestionReport>, everything is automagically set up and your queries can simply perform
var questions = repository.All(questionReport => questionReport.Question == "How old are you");
You should never use reflection in any kind of loop. It's very inefficient.
Anyway, if you're still set on this, you'll have to do the .All().ToList() then loop through it again to do your logic, because (to retiterate): you're doing it very, very wrong.
I am trying to figure out what this expression is:
allFeatures =
wellLayer.QueryTools.GetAllFeatures(ReturningColumnsType.AllColumns);
searchResults =
allFeatures.Where(f => f.ColumnValues["LEASE_NAME"].ToLower()
.Contains(searchQuery.ToLower())).ToList();
I was also wondering if there is a way to make this expression get multiple values - not only "LEASE_NAME" but a few others.
To get multiple values, try this
searchResults =
allFeatures.Where(f => f.ColumnValues["LEASE_NAME"].ToLower()
.Contains(searchQuery.ToLower()) ||
f.ColumnValues["SOMEOTHER_NAME"].ToLower()
.Contains(searchQuery.ToLower()) ||
f.ColumnValues["YETANOTHER_NAME"].ToLower()
.Contains(searchQuery.ToLower())).ToList();`
You can use the OR (||) and add as many values as you would like.
I'm building a dynamic query that can have n of Where method calls and n of SelectMany calls dependent upon user input. For example I may have:
var qZ = entityContext.TableA
.SelectMany(a=>a.TableB, (a,t)=>new{a,t} )
.Where(a=>a.t.FieldID==21)
.Where(a=> EntityFunctions.Left(a.t.Value,1)=="p")
.SelectMany(a=>a.a.TableC, (a,t)=>new{a,t} )
.Where(a=>a.t.FieldID==22)
.Where(a=> a.a.t.Value=="Peter" && a.t.Value=="Pan")
.Where(a=> a.a.a.TypeID==3)
.Select(a=> new{ a.a.a.ItemID }
).Distinct();
In the method I'm writing, I use helper methods that return an IQueryable as seen in the return line below.
return query.Provider.CreateQuery(
Expression.Call(typeof(Queryable),
"Where",
new Type[] {query.ElementType},
query.Expression, predicateLambda)
);
I'm able to create LambdaExpressions for all of the various query attribute-value pairs required, but I am unable to create one for the resultSelector of Queryable.SelectMany.
How can we create (a,t) => new{a=a, t=t} in an expression tree? Or How do we accomplish the same result as the .SelectMany above using Expression.Call like below?
Expression.Call(typeof(Queryable),
"SelectMany",
????????,
????????
);
I've tried using the SelectMany overload that doesn't require the resultSelector which works to some degree, however, I don't know how to reference the properties of t in subsequent method calls.
I've found this lambda expression ((a,t) => new{a=a, t=t}) associated with SelectMany all over the web, but I can't find any example of how to convert it to an expression tree.
UPDATE:
Let's reframe the question. I can pass the lambda like this
var q = entityContext.TableA.AsQueryable();
var q1 = Queryable.SelectMany(q, a => a.TableB, (a, t) => new { a = a, t = t });
var q2 = Queryable.Where(q1,a=>a.t.FieldID==22);
That works, however, since I don't know ahead of time how many SelectMany need to be called and since each call changes to anonymous type of the IQueriable, is there a way to cast (and re-cast) the anonymous type to a single variable? This way I can loop through and apply whatever method necessary to the variable and then enumerate to get the results once the query is built. Something like:
var q = entityContext.TableA..AsQueryable();
q = Queryable.SelectMany(q, a => a.TableB, (a, t) => new { a = a, t = t });
q = Queryable.Where(q,a=>a.t.FieldID==22);
(BTW: This doesn't work)
The way that I ended up resolving this required a paradigm shift. The first query above was based upon the fact that I learned to write queries by joining all the tables I needed together to give me access to filter on and select fields in those tables.
SelectMany() creates joins and the box around my thinking at the time required that if I need to filter on a specific column in a table, I had to join that table to my query. This in turn changed the type of my IQueryable resulting in my not being able to predict the Type of the IQueryable at design time.
Answer:
Step 1: Set the type of IQueryable to the output type it needs to return. In the case above, the result was always IQueryable.
Step 2: Utilize Expressions to dynamically create the WHERE predicate, including any and all tables necessary to create the proper filter. This always returns Expression> and all of the other variables we easily accounted for. And rememeber, in EF it isn't necessary to join table outside of Where() if they are only needed in Where().
I am trying to combine a multiple selection with a lambda function into an lambda expression. How do I do that? I know the last line is wrong, but giving you an idea of what I mean.
Func<Event, bool> where = null;
if (!string.IsNullOrWhiteSpace(searchToken))
where = q => q.Name.ToUpper().Contains(searchToken.ToUpper());
where += q => q.Hidden = false;
Expression<Func<Event, bool>> where1 = q => where; <-- Erroring
I suspect you want PredicateBuilder. (The source is available on that page.) You'd use it like this:
var predicate = q => !q.Hidden;
if (!string.IsNullOrWhiteSpace(searchToken))
{
predicate = predicate.And(q => q.Name.ToUpper()
.Contains(searchToken.ToUpper());
}
return predicate;
That's assuming you want to "and" the conditions - you never made that clear...
Note that that is not a good way to compare in a case-insensitive way, either. If you could tell us what's going to consume the query (e.g. LINQ to SQL, LINQ to EF) we could suggest a provider-compatible way of performing a case-insensitive query.
Look at http://msdn.microsoft.com/en-us/library/bb882637.aspx. How to use expression trees to build dynamic queries.
AFAIK when using Expression <> like that the expression must be known in compile time, because the compiler then build AST abstract syntax three and stores it as data in your Expression <> instance.
I needed to build a dynamic filter and I wanted to keep using entities. Because of this reason I wanted to use the PredicateBuilder from albahari.
I created the following code:
var invoerDatums = PredicateBuilder.True<OnderzoeksVragen>();
var inner = PredicateBuilder.False<OnderzoeksVragen>();
foreach (var filter in set.RapportInvoerFilter.ToList())
{
if(filter.IsDate)
{
var date = DateTime.Parse(filter.Waarde);
invoerDatums = invoerDatums.Or(o => o.Van >= date && o.Tot <= date);
}
else
{
string temp = filter.Waarde;
inner = inner.Or(o => o.OnderzoekType == temp);
}
}
invoerDatums = invoerDatums.And(inner);
var onderzoeksVragen = entities.OnderzoeksVragen
.AsExpandable()
.Where(invoerDatums)
.ToList();
When I ran the code there was only 1 filter which wasn't a date filter. So only the inner predicate was filled. When the predicate was executed I got the following error.
The parameter 'f' was not bound in the
specified LINQ to Entities query
expression.
While searching for an answer I found the following page. But this is already implemented in the LINQKit.
Does anyone else experienced this error and know how to solve it?
I ran across the same error, the issue seemed to be when I had predicates made with PredicateBuilder that were in turn made up of other predicates made with PredicateBuilder
e.g. (A OR B) AND (X OR Y) where one builder creates A OR B, one creates X OR Y and a third ANDs them together.
With just one level of predicates AsExpandable worked fine, when more than one level was introduced I got the same error.
I wasn't able to find any help but through some trial and error I was able to get things to work.
Every time I called a predicate I followed it with the Expand extension method.
Here is a bit of the code, cut down for simplicity:
public static IQueryable<Submission> AddOptionFilter(
this IQueryable<Submission> query,
IEnumerable<IGrouping<int, int>> options)
{
var predicate = options.Aggregate(
PredicateBuilder.False<Submission>(),
(accumulator, optionIds) => accumulator.Or(ConstructOptionMatchPredicate(optionIds).Expand()));
query = query.Where(predicate.Expand());
return query;
}
Query is an IQueryable which has already had AsExpandable called, ConstructOptionNotMatchPredicate returns an Expression.
Once we got past the error we were certainly able to build up complicated filters at runtime against the entity framework.
Edit:
Since people are still commenting on and up voting this I assume it is still useful so I am sharing another fix. Basically I have stopped using LinqKit and it's predicate builder in favour of this Universal Predicate Builder that has the same API but doesn't need Expand calls, well worth checking out.
I got this error and Mant101's explanation got me the answer, but you might be looking for a simpler example that causes the problem:
// This predicate is the 1st predicate builder
var predicate = PredicateBuilder.True<Widget>();
// and I am adding more predicates to it (all no problem here)
predicate = predicate.And(c => c.ColumnA == 1);
predicate = predicate.And(c => c.ColumnB > 32);
predicate = predicate.And(c => c.ColumnC == 73);
// Now I want to add another "AND" predicate which actually comprises
// of a whole list of sub-"OR" predicates
if(keywords.Length > 0)
{
// NOTICE: Here I am starting off a brand new 2nd predicate builder....
// (I'm not "AND"ing it to the existing one (yet))
var subpredicate = PredicateBuilder.False<Widget>();
foreach(string s in keywords)
{
string t = s; // s is part of enumerable so need to make a copy of it
subpredicate = subpredicate.Or(c => c.Name.Contains(t));
}
// This is the "gotcha" bit... ANDing the independent
// sub-predicate to the 1st one....
// If done like this, you will FAIL!
// predicate = predicate.And(subpredicate); // FAIL at runtime!
// To correct it, you must do this...
predicate = predicate.And(subpredicate.Expand()); // OK at runtime!
}
Hope this helps! :-)