views:

97

answers:

1

I have a set of POCOs, all of which implement the following simple interface:

 interface IIdObject
 {
     int Id { get; set; }
 }

A subset of these POCOs implement this additional interface:

 interface IDeletableObject : IIdObject
 {
     bool IsDeleted { get; set; }
 }

I have a repository hierarchy that looks something like this:

IRepository<T> <: BasicRepository<T> <: ValidatingRepository<T> (where T is IIdObject)

I'm trying to add a FilteringRepository to the hierarchy such that all of the POCOs that implement IDeletableObject have a Where(p => p.IsDeleted == false) filter applied before any other queries take place. My goal is to avoid duplicating the hierarchy solely for IDeletableObjects.

My first attempt looked like this:

public override IQueryable<T> Query()
{
    return base.Query().Where(t => ((IDeletableObject)t).IsDeleted == false);
}

This works well with LINQ to Objects, but when I switch to an EF backend I get: "LINQ to Entities only supports casting Entity Data Model primitive types."

I went on to try some fancier parameterized solutions, but they ultimately failed because I couldn't make T covariant in the following case for some reason I don't quite understand:

interface IQueryFilter<out T>  // error
{
    Expression<Func<T, bool>> GetFilter();
}

I'd be happy to go into more detail on my more complicated solutions if it would help, but I think I'll stop here for now in hope that someone might have an idea for me to try.

Thanks very much in advance!

+1  A: 

This is too big for comment, so...

You can create expressions dynamically. I've created helper methods:

public static class ExpressionHelper
{
    public static MemberExpression PropertyExpression(this Expression expr,string propertyName)
    {           
        var properties = propertyName.Split('.');

        MemberExpression expression = null;

        foreach (var property in properties)
        {
            if (expression == null)
                expression = Expression.Property(expr, property);
            else
                expression = Expression.Property(expression, property);
        }

        return expression;
    }

    public static BinaryExpression EqualExpression<T>(this Expression expr, string propertyName, T value)
    {
        return Expression.Equal(expr.PropertyExpression(propertyName), Expression.Constant(value, typeof(T)));
    }
}

Then you can use:

//Checking if T implements IDeletableObject
if (typeof(IDeletableObject).IsAssignableFrom(typeof(T)))
{
    //a
    var parameter = Expression.Parameter(typeof(T), "a");
    //a.IsDeleted == false
    var where = parameter.EqualExpression("IsDeleted", false);
    //a => a.IsDeleted == false
    var condition = Expression.Lambda<Func<T, bool>>(where, parameter);
    list = list.Where(condition);
}

EDIT

You can also use Dynamic Linq Library. It uses expressions too, but doesn't force you to think about how it all works, just write simple conditions as string. I don't know how it handles bool values.

LukLed
Super interesting (and I greatly appreciate the hand-holding, heh). I'll try this out ASAP!
ladenedge
@ladenedge: It should work fine, but may need some adjustments. I wasn't able to test it completely.
LukLed
Well, I played a bit with your code here, but Dynamic Linq was just too easy, heh. The resulting code looks like: `set.Where("IsDeleted == @0", false)` - very straightforward!Thanks so much for your time and help!
ladenedge
@ladenedge: Playing a bit and then using dynamic linq was propably the best solution:)
LukLed