简体   繁体   English

如何使用表达式基于继承的接口属性对集合进行排序

[英]How to use expressions to sort a collection based on an inherited interface property

This problem has been discussed to an extent in this question: Create Generic Expression from string property name but perhaps I'm missing the answer, or it is subtly different. 在以下问题中已在某种程度上讨论了此问题: 从字符串属性名称创建通用表达式,但可能我错过了答案,或者它有所不同。

I have the following queryable extension method: 我有以下可查询的扩展方法:

public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
    {
        var sourceType = typeof (TSource);
        var parameter = Expression.Parameter(sourceType, "item");
        var orderByProperty = Expression.Property(parameter, propertyName);
        var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
        return Queryable.OrderByDescending(source, (dynamic) orderBy);
    }

For the purposes of this problem let us assume that the queryable source instance has a property called 'Created' which is a type of DateTime. 出于此问题的目的,我们假设可查询源实例具有一个名为“ Created”的属性,该属性是DateTime的一种类型。 If we define a class that has the property 'Created' on it and then OrderByDescending then the above will work fine. 如果我们定义一个具有“ Created”属性的类,然后定义OrderByDescending,则以上内容将可以正常工作。 eg 例如

var queryable = new List<EntityClassWithCreatedProperty>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();

If we do the same thing but with an interface such as ICreated which has the Created property on it: 如果我们做同样的事情,但是具有ICreated之类的接口,该接口上具有Created属性:

public interface ICreated
{
    DateTime Created { get; }
}

Then the following also works: 然后,以下内容也适用:

var queryable = new List<ICreated>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();

If however, you have the following interface hierarchy: 但是,如果您具有以下接口层次结构:

public interface ITimestamped : ICreated
{
     ...
}

Then the following will fail: 然后,以下操作将失败:

var queryable = new List<ITimestamped>().AsQueryable();
var result = queryable.OrderByPropertyDescending("Created").ToList();

With the similar error message to that of the other question: Instance property 'Created' is not defined for type ITimestamped. 出现与另一个问题类似的错误消息:未为ITimestamped类型定义实例属性'Created'。 I'm assuming that I need to find the property on the declaring type of interface (which I can do by crawling the source type) but then what do I do with it? 我假设我需要在声明的接口类型上找到属性(可以通过爬网源类型来完成此操作),但是该怎么办呢? Most attempts I have tried result in the incorrect original source type not being cast-able back to the IQueryable. 我尝试过的大多数尝试都会导致错误的原始源类型无法强制转换回IQueryable。 Do I need to use a ConvertType call somewhere? 我需要在某个地方使用ConvertType调用吗? Thanks. 谢谢。

I would use the other Expression.Property() method that takes a propertyInfo, eg 我将使用另一个采用propertyInfo的Expression.Property()方法,例如

public static IQueryable<TSource> OrderByPropertyDescending<TSource>(IQueryable<TSource> source, string propertyName)
{
    var sourceType = typeof (TSource);
    var parameter = Expression.Parameter(sourceType, "item");
    var propertyInfo = FindMyProperty(sourceType, propertyName);
    var orderByProperty = Expression.Property(parameter, propertyInfo);
    var orderBy = Expression.Lambda(orderByProperty, new[] { parameter });
    return Queryable.OrderByDescending(source, (dynamic) orderBy);
}

private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
    return type.GetProperty(propertyName);
}

At this point your question has nothing to do with expressions, it's a simple reflection question, and you have to find the correct way to get the properties you want. 此时,您的问题与表达式无关,这是一个简单的反射问题,您必须找到获取所需属性的正确方法。 There are a lot of scenarios here. 这里有很多场景。 For your exact one, meaning get property from parent interface, you can do something like: 对于您的确切含义(即从父界面获取属性),您可以执行以下操作:

private static PropertyInfo FindMyProperty(Type type, string propertyName)
{
    var result = type.GetProperty(propertyName);
    if (result == null)
    {
        foreach(var iface in type.GetInterfaces())
        {
            var ifaceProp = FindMyProperty(iface, propertyName);
            if (ifaceProp != null)
                return ifaceProp;
        }

    }
    return result;
}

DISCLAIMER! 免责声明! This is by no means the best way to get a property from a type, but it should work in your case. 这绝不是从类型中获取属性的最佳方法,但它应适用于您的情况。 You should google around to find an implementation for FindMyProperty that satisfies all your requirements :) 您应该四处搜寻以找到满足您所有要求的FindMyProperty实现:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM