- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Dynamic Query Execution Using Predicate Builder
Hello all! I was wondering if something like a predicate builder would work with LLBLGen. Here is the code I am referring to: http://www.albahari.com/nutshell/predicatebuilder.html I created a method in my service layer that takes an expression that filters out the data. It works fine when I just pass in an expression as a lambda. When I start to use the Predicate builder I get the following:
Exception: System.ArgumentException Message: "Expression of type 'System.Boolean' cannot be invoked" Stack Trace:
at System.Linq.Expressions.Expression.Invoke(Expression expression, IEnumerable
1 arguments) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleInvocationExpression( InvocationExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder. HandleBinaryExpressionArithmeticOrBitOperator(BinaryExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleLambdaExpression(LambdaExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleWhereExpression(WhereExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleSelectExpression(SelectExpression expressionToHandle, SelectExpression newInstance) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleSelectExpression(SelectExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleSelectExpression(SelectExpression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.GenericExpressionHandler.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers.QueryExpressionBuilder.HandleExpression(Expression expressionToHandle) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.HandleExpressionTree(Expression expression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.Execute(Expression expression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProProviderBase.System.Linq.IQueryProvider.Execute(Expression expression) at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery
1.Execute() at SD.LLBLGen.Pro.LinqSupportClasses.LLBLGenProQuery1.SD.LLBLGen.Pro.LinqSupportClasses .ILLBLGenProQuery.Execute[TResult]() at TrackerPlus.Business.Views.VehicleView.GetVehiclesByExpression(Expression
1 whereClause) in C:\Projects\TrackerPlus\TrackerPlus.Business\Views\VehicleView.cs:line 46 ... my stuff that calls your stuff was here
I am using v2.6 Final, Released on June 6th, 2008. Targeting .NET 3.5 adapter model.
Is there a different way of doing this? I also tried the following variant of the And<> extension method with the following result:
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
//var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.And(expr1.Body, expr2.Body), expr1.Parameters);
}
Exception: ORMQueryExecutionException InnerException: An object or column name is missing or empty. For SELECT INTO statements, verify each column has a name. For other statements, look for empty alias names. Aliases defined as \"\" or [] are not allowed. Add a name or single space as the alias name. QueryExecuted:
Query: SELECT DISTINCT [LPLA_1].[VehicleId], [LPLA_1].[VIN] AS [Vin], [LPLA_1].[VehicleStatusId], [LPLA_1].[StickerNumber], [LPLA_1].[VehicleNumber], [LPLA_1].[Description], [LPLA_1].[MakeId], [LPLA_1].[ModelId], [LPLA_1].[Year], [LPLA_1].[VehicleTypeId], [LPLA_1].[OwnedBy], [LPLA_1].[AssignedTo], [LPLA_1].[LessorId], [LPLA_1].[LeaseNumber], [LPLA_1].[LeaseYears], [LPLA_1].[LeaseStart], [LPLA_1].[LeaseExpiration], [LPLA_1].[LeaseMiles], [LPLA_1].[PurchasePrice], [LPLA_1].[Residual], [LPLA_1].[LeasePayment], [LPLA_1].[LeaseEndCharge], [LPLA_1].[EmployeeAssigned], [LPLA_1].[PolicyRemovalDate], [LPLA_1].[RegistrationDate], [LPLA_1].[ExpirationDate], [LPLA_1].[DecalWeight], [LPLA_1].[PurchaseDate], [LPLA_1].[PlateNumber], [LPLA_1].[StateCode], [LPLA_1].[RegAddress], [LPLA_1].[RegCity], [LPLA_1].[RegZip], [LPLA_1].[FuelTypeId], [LPLA_1].[Created], [LPLA_1].[Updated], [LPLA_1].[AddedToPolicy]
FROM [dbo].[Vehicle] [LPLA_1] WHERE ( ( ( ( ( [].[LPFA_1] = @LPFA_11)))))
Parameter: @LPFA_11 : Boolean. Length: 0. Precision: 0. Scale: 0. Direction: Input. Value: True.
My Service Layer call:
public EntityCollection<VehicleEntity> GetVehiclesByExpression(Expression<Func<VehicleEntity, bool>> whereClause)
{
using (IDataAccessAdapter adapter = this._adapter.Create())
{
LinqMetaData metaData = new LinqMetaData(adapter);
return metaData.Vehicle
.Where<VehicleEntity>(whereClause)) as ILLBLGenProQuery)
.Execute<EntityCollection<VehicleEntity>>())
}
}
I do some prefetching of other related entities but ommitted that code to simplify the question. Any thoughts?
Don't use the predicate builder, it's using Invoke which is simply hte same as calling a delegate in-memory, which isn't what you want you want the predicate to end up in the db query. The problem is that the 'Invoke' means calling the compiled code, but that's not what's meant: the data inside the expression invoked has to be converted.
I also don't have a clear overview of what is wrong exactly in your second part of the post: please provide the info so we can try to reproduce what you have.
Also, please specify why you do what you are doing, and why you can't use normal linq constructs with linq to llblgen pro which do work.
I am doing a report builder with custom predicates. I don't want you to use up too much time working this out because I know how to get it done using the standard LLBLGen predicate system. I just wanted to know if there was something that could be done similar to the predicate builder but in LLBLGen. Again, I've already got it working using the wonderful predicate builder that Linq to LLBLGen is built on top of. It was more of a curiosity thing. The first example showed how the predicate builder does not work. The second was my attempt at building the expression in a different way using my own stuff.
Thanks.
No problem Seth
I'll see if I can get it to work and if not, what it does behind the scenes to the expression trees so I have a better overview and if I have to add something to make it work
Otis wrote:
No problem Seth
![]()
I'll see if I can get it to work and if not, what it does behind the scenes to the expression trees so I have a better overview and if I have to add something to make it work
![]()
Any news? I'm also trying to build a dynamic predicate system, to combine expression with and/or.
It simply chokes on the InvocationExpression instances created by the predicatebuilder. Frankly I have no idea why they create these expressions, it might be something that works just with linq to sql which might deal with the invocationexpressions.
I'll spend a brief moment checking whether I can either change the predicatebuilder source or add some support for invocationexpressions in this case, but you all have to realize that this is very very very frustrating. We spend 9 months full time writing a linq provider without any documentation whatsoever, and figuring out how 3rd party software works on linq to sql so we can copy the behavior on our own framework is therefore a matter of tedious trial/error and that added to the countless times we already had to use that way of doing software engineering with linq and expression trees, it's getting a bit too much. We did our very best to provide the best 3rd party linq provider out there, but I have to say there's a limit to what we can do.
The invocationexpression instances in the tree apparently have to be converted into something, as the predicate expression IS created but the handler of the invocationexpression of that produced predicateexpression gives the error in the generic sourcecode for visiting expression trees: the FilterExpression (derived from Expression) isn't invokable because it's not an Expression<T> class.
Anyone who knows WHY on earth this doesn't work, please step forward.
(btw, the predicatebuilder contains 2 bugs as well, 'And' should be 'AndAlso' and 'Or' should be 'OrElse', because 'Or' is a bitwise or operator predicate. )
(edit). The invoke is apparently used to replace parameters in the rightside expression (expr2) with the parameters of expr1. I can do that differently. it now produces a query, but the alias is wrong due to the wrong parameter. The utility class 'ExpressionReplacer' in the linq provider will help )
Works.
[Test]
public void GetCustomersUsingPredicateBuilder()
{
var predicate = PredicateBuilder.Null<CustomerEntity>();
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData metaData = new LinqMetaData(adapter);
predicate = predicate.Or(c => c.City == "London");
predicate = predicate.Or(c => c.City == "Paris");
var q = metaData.Customer.Where(predicate);
foreach(var v in q)
{
Assert.IsTrue((v.City == "London") || (v.City == "Paris"));
}
}
}
New predicate builder class:
public static class PredicateBuilder
{
public static System.Linq.Expressions.Expression<Func<T, bool>> Null<T>() { return null; }
public static System.Linq.Expressions.Expression<Func<T, bool>> Or<T>(this System.Linq.Expressions.Expression<Func<T, bool>> expr1,
System.Linq.Expressions.Expression<Func<T, bool>> expr2)
{
if(expr1 == null)
{
return expr2;
}
ExpressionReplacer replacer = new ExpressionReplacer(CreateFromToReplaceSet(expr2.Parameters, expr1.Parameters), null, null, null, null);
LambdaExpression rightExpression = (LambdaExpression)replacer.HandleExpression(expr2);
return System.Linq.Expressions.Expression.Lambda<Func<T, bool>>
(System.Linq.Expressions.Expression.OrElse(expr1.Body, rightExpression.Body), expr1.Parameters);
}
public static System.Linq.Expressions.Expression<Func<T, bool>> And<T>(this System.Linq.Expressions.Expression<Func<T, bool>> expr1,
System.Linq.Expressions.Expression<Func<T, bool>> expr2)
{
if(expr1 == null)
{
return expr2;
}
ExpressionReplacer replacer = new ExpressionReplacer(CreateFromToReplaceSet(expr2.Parameters, expr1.Parameters), null, null, null, null);
LambdaExpression rightExpression = (LambdaExpression)replacer.HandleExpression(expr2);
return System.Linq.Expressions.Expression.Lambda<Func<T, bool>>
(System.Linq.Expressions.Expression.AndAlso(expr1.Body, rightExpression.Body), expr1.Parameters);
}
private static Dictionary<LinqExpression, LinqExpression> CreateFromToReplaceSet(IList<ParameterExpression> from,
IList<ParameterExpression> to)
{
Dictionary<LinqExpression, LinqExpression> toReturn = new Dictionary<LinqExpression, LinqExpression>();
for(int i = 0; i < from.Count; i++)
{
toReturn.Add(from[i], to[i]);
}
return toReturn;
}
}
required usings using LinqExpression = System.Linq.Expressions.Expression; using SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers;
**Please let me know if this works for you. **
Sorry for the whining in my previous post, though it sometimes get on my nerves how shabby the whole documentation around everything linq is...
I did a wild guess with the lambda changes and it worked out ok. haven't tested it further.
You see that it doesn't have True/False methods anymore, this is because those would result in a constant bool as predicate, which isn't supported.
Otis wrote:
No-one who wants to give a reply?
It works for me! Now I have a method that combines (with AND/OR) several filter criteria to search articles:
public static IQueryable GetArticoliUsingPredicateBuilder(DataAccessAdapter adapter, LogicalOperator op,
string codice,
string descrizione,
int? idCategoria,
int? idProduttore,
int? idLineaProdotto,
int? idFornitore,
bool soloAttivi)
{
var predicate = PredicateBuilder.Null<Genio.Data.Adapter.EntityClasses.ArticoliEntity>();
Genio.Data.Adapter.Linq.LinqMetaData metaData = new Genio.Data.Adapter.Linq.LinqMetaData(adapter);
if (!String.IsNullOrEmpty(codice))
{
if (op == LogicalOperator.Or)
{
predicate = predicate.Or(a => a.CodiceArticolo.Contains(codice) || a.CodiceFornitore.Contains(codice) || a.CodiceAbarre.Contains(codice)).Or(
art => art.BarcodeArticolo.Any(
bc => bc.Barcode.Contains(codice)));
}
else predicate = predicate.And(a => a.CodiceArticolo.Contains(codice) || a.CodiceFornitore.Contains(codice) || a.CodiceAbarre.Contains(codice)).Or(
art => art.BarcodeArticolo.Any(
bc => bc.Barcode.Contains(codice)));
}
if (!String.IsNullOrEmpty(descrizione))
{
if (op == LogicalOperator.Or)
predicate = predicate.Or(a => a.DescrizioneBreve.Contains(descrizione) || a.DescrizioneEstesa.Contains(descrizione));
else predicate = predicate.And(a => a.DescrizioneBreve.Contains(descrizione) || a.DescrizioneEstesa.Contains(descrizione));
}
if (idCategoria.HasValue)
{
if (op == LogicalOperator.Or)
predicate = predicate.Or(a => a.IdcategoriaArticolo == idCategoria);
else predicate = predicate.And(a => a.IdcategoriaArticolo == idCategoria);
}
if (idProduttore.HasValue)
{
if (op == LogicalOperator.Or)
predicate = predicate.Or(a => a.Idproduttore == idProduttore);
else predicate = predicate.And(a => a.Idproduttore == idProduttore);
}
if (idLineaProdotto.HasValue)
{
if (op == LogicalOperator.Or)
predicate = predicate.Or(a => a.IdlineaProdotto == idLineaProdotto);
else predicate = predicate.And(a => a.IdlineaProdotto == idLineaProdotto);
}
if (idFornitore.HasValue)
{
if (op == LogicalOperator.Or)
predicate = predicate.Or(a => a.IdclienteFornitorePrincipale == idFornitore);
else predicate = predicate.And(a => a.IdclienteFornitorePrincipale == idFornitore);
}
if(soloAttivi)
predicate = predicate.And(a => a.ArticoloAttivo == true && a.VirtualDeleted == false);
else predicate = predicate.And(a => a.VirtualDeleted == false);
PrefetchPath2 pp2 = new PrefetchPath2(EntityType.ArticoliEntity);
pp2.Add(ArticoliEntity.PrefetchPathBarcodeArticolo);
var q = metaData.Articoli.Where(predicate).WithPath(pp2).IncludeFields(
art => art.Idarticolo,
art => art.CodiceArticolo,
art => art.DescrizioneBreve,
art => art.CodiceAbarre,
art => art.CodiceFornitore,
art => art.Idproduttore,
art => art.IdclienteFornitorePrincipale,
art => art.IdcategoriaArticolo,
art => art.IdlineaProdotto,
art => art.ArticoloAttivo);
return q;
}
thanks
Joined: 09-Sep-2008
Joined: 10-Sep-2008
Hi there
I'm Joe, the guy who wrote PredicateBuilder.
Regarding InvocationExpressions, they're simply a way of calling another lambda expression. It shouldn't be hard to support that within your LINQ parser. Having said that, Entity Framework can't handle InvocationExpressions either, so maybe you're in good company (actually I'd argue you could in bad company but that's another story).
Cheers
Joe
joealbahari wrote:
Hi there
I'm Joe, the guy who wrote PredicateBuilder.
Regarding InvocationExpressions, they're simply a way of calling another lambda expression. It shouldn't be hard to support that within your LINQ parser. Having said that, Entity Framework can't handle InvocationExpressions either, so maybe you're in good company (actually I'd argue you could in bad company
but that's another story).
Cheers
Joe
Hi Joe!
We do have support for them for some cases but not for the case you used them for. Indeed, adding support for them isn't that hard per se, the thing is that they might end up in various situations so figuring out all situations are covered is a different matter, unfortunately.
Luckily it didn't take that long to rewrite your idea to make it work on our code Thanks for the initial class!
Otis: You are free to call me a loser (after all I said I would look at it like forever ago) because I finally got around to using the predicate builder and it works like a charm. I think stylistically it is a bit more aesthetically pleasing. Thanks for the good work!
pat wrote:
Otis wrote:
New predicate builder class:
Cool... maybe this could even become part of the LLBLGen Linq support assemblies?
Thanks, Patrick
It's indeed a good idea to add this to the lib.
Joined: 14-Oct-2008
Hi all,
I wanted to implement the Predicate Builder class, but I can't resolve the LinqExpression type
private static Dictionary<LinqExpression, LinqExpression> CreateFromToReplaceSet(
I have included the 2 dll's and placed the usings.
Please help
Kind regards, Wim
hypo wrote:
Hi all,
I wanted to implement the Predicate Builder class, but I can't resolve the LinqExpression type
private static Dictionary<LinqExpression, LinqExpression> CreateFromToReplaceSet(
I have included the 2 dll's and placed the usings.
Please help
![]()
Kind regards, Wim
try adding:
using LinqExpression = System.Linq.Expressions.Expression;
edit: Otis is faster than me!
Joined: 14-Oct-2008
Otis wrote:
required usings using LinqExpression = System.Linq.Expressions.Expression; using SD.LLBLGen.Pro.LinqSupportClasses.ExpressionHandlers;
I should start reading the posts a bit better
Still thanks to both for the quick and correct reply
Kind regards, Wim
Joined: 09-Aug-2010
Hi there. Is there any advantage to using the Predicate Builder over say, doing something like the following?
using (DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData meta = new LinqMetaData(adapter);
var involvedParties = from ip in meta.InvolvedParty
select ip;
if (timeZoneID.HasValue)
involvedParties = involvedParties.Where(ip => ip.Contact.TimeZoneID == timeZoneID.Value);
if (stateID.HasValue)
involvedParties = involvedParties.Where(ip => ip.Contact.City.CountryStateID == stateID.Value);
if (specialtyID.HasValue)
involvedParties = involvedParties.Where(ip => ip.SpecialtyID == specialtyID.Value);
if (!string.IsNullOrEmpty(zipCode))
involvedParties = involvedParties.Where(ip => ip.Contact.ZipCode == zipCode);
var data = from ip in involvedParties
select new
{
ip.InvolvedPartyID,
ip.Contact.CompanyName,
ip.IsAutoApprovedClient
};
return data.ToList();
}
Since the above code works just fine (and runs just a single SQL statement on the server, without any client side filtering), I'm unclear as to why I should consider using the Predicate Builder at the moment. Thanks in advance for the advice.
carni4 wrote:
Since the above code works just fine (and runs just a single SQL statement on the server, without any client side filtering), I'm unclear as to why I should consider using the Predicate Builder at the moment. Thanks in advance for the advice.
You can take advantage of PredicateBuilder (IMHO) if you need to isolate the predicate for any reason (you receive the predicate in a fetch method, or you need to pass the predicate over the wire, or your method does know nothing about the filter parameters and it have to call other method to obtain the predicate). In such cases you could consider use it. Consider the following code:
public void GetCustomersUsingPredicateBuilder()
{
var predicate = PredicateBuilder.Null<CustomerEntity>();
using(DataAccessAdapter adapter = new DataAccessAdapter())
{
LinqMetaData metaData = new LinqMetaData(adapter);
predicate = predicate.Or(c => c.City == "London");
predicate = predicate.Or(c => c.City == "Paris");
var q = metaData.Customer.Where(predicate);
foreach(var v in q)
{
Assert.IsTrue((v.City == "London") || (v.City == "Paris"));
}
}
}
Note that predicate is independent, you use it just in the metaData.Customer.Where(predicate) line.
Joined: 09-Aug-2010
Ok, fair enough; that makes sense. However, for cases that don't require a separate object that can be passed around, is there by chance any difference in performance, the SQL that is output, or how the LINQ/ORM engine handles it?
Most importantly: I want to make sure that I'm not taking advantage of any sort of unsupported behavior that might break in a future release of LLBLGen. I mean, I was wondering why I hadn't seen any similar examples posted by other people. When I search on LLBLGen for 'conditional parameters in a LINQ where clause' (or similar search query) the only thing I can find is the PredicateBuilder, and I wanted to see if there is a good reason for that. The last thing I want to do is write a bunch of LINQ queries my way because it works now, only to find out that this breaks later because I was doing something not supported officially by LLBLGen.