Does LLBL Support This?

Posts   
 
    
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 04-Jan-2011 23:13:18   

Just trying to figure out if LLBL supports the following scenario. I have a bunch of tables I need to perform the exact predicate against when selecting data. Rather than duplicate this logic, I would like my entites to implement an interface then created a predicate against this.

for example, the following url shows a "generic predicate" pattern I want to utilize. I tried a quick stab at it, but it failed, so I am just curious if it is even supported before I go digging farther.

http://www.albahari.com/nutshell/predicatebuilder.aspx

thanks!

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 05-Jan-2011 03:12:39   

The page you mention shows how to use Predicate Builder to build dynamic linq predicates. However it doesn't show how to create a "generic" predicate builder routine.

Do you want to use Predicate Builder? Take a look at this: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=14144&StartAtMessage=0&#78965

David Elizondo | LLBLGen Support Team
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 05-Jan-2011 14:28:32   

daelmo wrote:

The page you mention shows how to use Predicate Builder to build dynamic linq predicates. However it doesn't show how to create a "generic" predicate builder routine.

Do you want to use Predicate Builder? Take a look at this: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=14144&StartAtMessage=0&#78965

That link does, just scroll down towards the bottom.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 05-Jan-2011 15:40:49   

The link opens another thread and scroll down to a specific message.

MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 05-Jan-2011 16:18:10   

Walaa wrote:

The link opens another thread and scroll down to a specific message.

Sorry, wrong link. The link from my original post.

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 05-Jan-2011 17:32:05   
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 06-Jan-2011 01:22:37   

Walaa wrote:

Please check the following thread: http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=15311

Ok, but can this be done through the Linq provider (IQueryable)?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 06-Jan-2011 06:22:14   

Hi there.

Using Linq, if you want dynamic predicate, you should use PredicateBuilder (my link above). If you want to create a method that inject a predicate to an IQueryable, that would be a bit of a problem because you have not the explicit type to use it in the expression.

Could you please show us an example of what you really want to achieve?

David Elizondo | LLBLGen Support Team
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 06-Jan-2011 15:13:39   

daelmo wrote:

Hi there.

Using Linq, if you want dynamic predicate, you should use PredicateBuilder (my link above). If you want to create a method that inject a predicate to an IQueryable, that would be a bit of a problem because you have not the explicit type to use it in the expression.

Could you please show us an example of what you really want to achieve?

In that link i specified, he shows how to create "Generic Predicates", basically having your entites implement an interface and they building an expression off the interface.

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 06-Jan-2011 21:50:42   

Sorry, I still can't see in that article where the entities are implementing any interfaces at all - we're still on entirely clear what you are trying to acheive.

You said in your original post that you had tried something that didn't work - perhaps you could show us what you tried and why it failed...?

Matt

MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 06-Jan-2011 21:55:53   

MTrinder wrote:

Sorry, I still can't see in that article where the entities are implementing any interfaces at all - we're still on entirely clear what you are trying to acheive.

You said in your original post that you had tried something that didn't work - perhaps you could show us what you tried and why it failed...?

Matt

Here is the section:

Generic Predicates Suppose every table in your database has ValidFrom and ValidTo columns as follows:

create table PriceList ( ID int not null primary key, Name nvarchar(50) not null, ValidFrom datetime, ValidTo datetime ) To retrieve rows valid as of DateTime.Now (the most common case), you'd do this:

from p in PriceLists where (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now) select p.Name Of course, that logic in bold is likely to be duplicated across multiple queries! No problem: let's define a method in the PriceList class that returns a reusable expression:

public static Expression<Func<PriceList, bool>> IsCurrent() { return p => (p.ValidFrom == null || p.ValidFrom <= DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now); } OK: our query is now much simpler:

var currentPriceLists = db.PriceLists.Where (PriceList.IsCurrent()); And with PredicateBuilder's And and Or methods, we can easily introduce other conditions:

var currentPriceLists = db.PriceLists.Where ( PriceList.IsCurrent().And (p => p.Name.StartsWith ("A"))); But what about all the other tables that also have ValidFrom and ValidTo columns? We don't want to repeat our IsCurrent method for every table! Fortunately, we can generalize our IsCurrent method with generics.

The first step is to define an interface:

public interface IValidFromTo { DateTime? ValidFrom { get; } DateTime? ValidTo { get; } } Now we can define a single generic IsCurrent method using that interface as a constraint:

public static Expression<Func<TEntity, bool>> IsCurrent<TEntity>() where TEntity : IValidFromTo { return e => (e.ValidFrom == null || e.ValidFrom <= DateTime.Now) && (e.ValidTo == null || e.ValidTo >= DateTime.Now); } The final step is to implement this interface in each class that supports ValidFrom and ValidTo. If you're using Visual Studio or a tool like SqlMetal to generate your entity classes, do this in the non-generated half of the partial classes:

public partial class PriceList : IValidFromTo { } public partial class Product : IValidFromTo { }

MTrinder
User
Posts: 1461
Joined: 08-Oct-2008
# Posted on: 06-Jan-2011 22:00:40   

OK, so what didn't work when you tried this ?

Matt

MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 06-Jan-2011 22:03:18   

MTrinder wrote:

OK, so what didn't work when you tried this ?

Matt

No, it did not. When I looked at the expression in the IDE it was tyring to do the following:


Convert(x).Date

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 07-Jan-2011 10:07:24   

MTrinder wrote:

OK, so what didn't work when you tried this ?

MarcoP wrote:

No, it did not. When I looked at the expression in the IDE it was tyring to do the following: Code: Convert(x).Date

I can't understand your answer to Matt's question.

MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 07-Jan-2011 15:18:30   

Walaa wrote:

MTrinder wrote:

OK, so what didn't work when you tried this ?

MarcoP wrote:

No, it did not. When I looked at the expression in the IDE it was tyring to do the following: Code: Convert(x).Date

I can't understand your answer to Matt's question.

When you hover over the expression being specified to the Where extension method, in the Visual Studio IDE shows the above snippet of code. Obviously that is wrong.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 10-Jan-2011 01:12:09   

I reproduced your behavior (using latest v3 RTL).

Additional interface for OrderEntity

namespace Northwind.EntityClasses
{   
    public partial class OrderEntity : IDatedOrder
    {
    }
}

namespace Northwind 
{
    public interface IDatedOrder
    {
        DateTime? OrderDate { get; }
    }
}

Generic filter method

public static Expression<Func<TEntity, bool>> IsCurrent<TEntity>()
where TEntity : IDatedOrder
{
    return e => (e.OrderDate < DateTime.Now);
}

Usage

using (DataAccessAdapter adapter = new DataAccessAdapter())
{
    LinqMetaData metaData = new LinqMetaData(adapter);
    var q = metaData.Order.Where(IsCurrent<OrderEntity>());

    var result = q.ToList();
}

Exception Message (Stack trace attached)

Test method Tests.UnitTest1.GenericLinqExpression threw exception: SD.LLBLGen.Pro.ORMSupportClasses.ORMQueryConstructionException: The binary expression '(Convert(Convert([507]).OrderDate) < Convert(Convert(09/01/2011 06:02:37 p.m.)))' can't be converted to a predicate expression..

To be honest, I don't know if this is easily possible. I will ask the LLBLGen Team for a recommendation. You also could use a FunctionMapping to simulate a generic predicate construction, then you just use that function in your linq queries (see the docs for more info).

Attachments
Filename File size Added on Approval
stackTrace.txt 4,543 10-Jan-2011 01:13.49 Approved
David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 10-Jan-2011 16:18:29   

I think the error happens because the member access (507 is the numeric value for LLBLGenProExpressionType.Entity, so the expression object is an 'entity') has to be evaluated before the binary expression can be evaluated, however in your case it can't do that because it only knows the member expression after it has actually ran the method. This is a catch22.

Perhaps, but haven't tried it: var predicate = IsCurrent<OrderEntity>(); using (DataAccessAdapter adapter = new DataAccessAdapter()) { LinqMetaData metaData = new LinqMetaData(adapter); var q = metaData.Order.Where(predicate);

var result = q.ToList();

}

will work, as the predicate is then already known.

Frans Bouma | Lead developer LLBLGen Pro
MarcoP avatar
MarcoP
User
Posts: 270
Joined: 29-Sep-2004
# Posted on: 10-Jan-2011 19:08:42   

thanks guy for looking into this.