开发者

EF/LINQ: Where() against a property of a subtype

开发者 https://www.devze.com 2023-01-02 17:57 出处:网络
I have a set of POCOs, all of which implement the following simple interface: interface IIdObject { int Id { get; set; }

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 wor开发者_如何学运维ks 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!


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消