views:

32

answers:

2

Suppose I have two tables, TableA and TableB. Each record in A has one or more related records in B. Say I want a reusable filter using predicates. I might do something like this (Linq-to-SQL by the way):

private Expression<Func<ARecord, bool>> FilterPredicate()
{
    return x => x.Name == "Test";
}

private IQueryable<ARecord> GetRecords()
{
    return DataContext.TableA.Where(FilterPredicate());
}

That works fine, but say I wanted to search TableB, but use the same "filter" on the foreign key. I want to accomplish the query below without having to rewrite FilterPredicate for how it relates to TableB.

var query = from b in DataContext.B
            where b.A.Name == "Test"
            select b;

I'm just wondering if there are any best practices for creating reusable "where" clauses that would help across multiple tables.

Edit - To clarify, I'm not looking for a way to apply the predicate to ARecord and BRecord types. I'm looking for a way (any way, not necessarily along the lines I was already thinking of) to prevent needing this predicate as well:

private Expression<Func<BRecord, bool>> FilterPredicate2()
{
    return x => x.A.Name == "Test";
}

Thanks in advance.

+1  A: 

You can do this by defining an interface over A and B.

public interface IHasName // contrived, I know
{
    string Name {get;}
}

LINQ-To-SQL classes are partial, so in your part of the partial class definition, you can add the interface like so:

public partial class A : IHasName {}
public partial class B : IHasName {}

As you see, no implementation should be needed since the Name property is implemented in the Linq-To-Sql generated part.

Now constrain your predicate to types implementing the IHasName interface, and you're all set:

private Expression<Func<T, bool>> FilterPredicate(string name) where T : IHasName
{
    return x => x.Name == name;
}

you should now even be able to define an extension method on IQueryable like so:

public static T GetByName<T>(this IQueryable<T> queryable, 
                             string name) where T : IHasName
{
    return queryable.Where(FilterPredicate(name)).SingleOrDefault();
}

Small caveat: of course, the property in the interface ('Name') must exactly match the property name in the implementing classes. Suppose you have a class C with property 'MyName'. You might be tempted to implement the IHasName interface like so:

public partial class C : IHasName
{
    public string Name {return MyName;} 
} 

This will of course not work, as the Linq-To-Sql expression parser will use 'Name' instead of the actual property 'MyName', so it won't be able to map this expression to valid SQL.

jeroenh
"This will of course not work, as the Linq-To-Sql expression parser will use 'Name' instead of the actual property 'MyName', so it won't be able to map this expression to valid SQL."Actually, there is a way around this - it's kinda painful, but it works - http://social.msdn.microsoft.com/Forums/en-US/linqtosql/thread/5691e0ad-ad67-47ea-ae2c-9432e4e4bd46
Frank Tzanabetis
Please see my edit. I'm looking to filter on the foreign key, not apply the same predicate to A and B types.
Ocelot20
A: 

I guess thinking more about it, it is somewhat of a stupid question. I was hoping to be able to use the cleaner query:

var query = from b in DataContext.B 
            select b;

And apply this to it:

x => x.A.Name == "Test"

Without having to have the duplicate this predicate that I use when starting the query on the A table:

x => x.Name == "Test"

So I suppose the solution is to "reverse" the query by starting on the A table, like so:

var query = from a in DataContext.A
            join b in B on a equals b.A
            select b;

query = query.Where(FilterPredicate());

I was thinking it may rewrite the queries inefficiently, but that doesn't seem to be the case.

Ocelot20