使用字段名称作为字符串

时间:2015-08-18 00:30:14

标签: c# linq entity-framework

我使用的是.NET 4.51,EF 6

我对我的存储库层进行了多次调用,我需要按升序或降序对单个字段进行基本排序,例如:

enter image description here

GetAllList()的结果是List<T>。现在遗憾的是,我必须排序的Id字段并不总是称为Id,也不是Text字段。它们可以是其他内容,例如MyIdSomeTextField等。

所以我想知道是否有一种方法可以通过为字段名称提供字符串来执行OrderBy()OrderByDescending()子句:

_Repository.GetAllList().OrderBy(r => r."SomeTextField")

通过这种方式,我可以将所有这些代码移动到一个通用的方法。

任何指针都非常感激。

4 个答案:

答案 0 :(得分:11)

这将有效:

public static class LinqExtensions 
{
    private static PropertyInfo GetPropertyInfo(Type objType, string name)
    {
        var properties = objType.GetProperties();
        var matchedProperty = properties.FirstOrDefault (p => p.Name == name);
        if (matchedProperty == null)
            throw new ArgumentException("name");

        return matchedProperty;
    }
    private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi)
    {
        var paramExpr = Expression.Parameter(objType);
        var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
        var expr = Expression.Lambda(propAccess, paramExpr);
        return expr;
    }

    public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> query, string name)
    {
        var propInfo = GetPropertyInfo(typeof(T), name);
        var expr = GetOrderExpression(typeof(T), propInfo);

        var method = typeof(Enumerable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
        var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);     
        return (IEnumerable<T>) genericMethod.Invoke(null, new object[] { query, expr.Compile() });
    }

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string name)
    {
        var propInfo = GetPropertyInfo(typeof(T), name);
        var expr = GetOrderExpression(typeof(T), propInfo);

        var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
        var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);     
        return (IQueryable<T>) genericMethod.Invoke(null, new object[] { query, expr });
    }
}

测试:

var r = new List<temp> { 
    new temp { a = 5 }, 
    new temp { a = 1 }, 
    new temp { a = 15 }
}.OrderBy("a");

给出正确的结果(1,5,15) - 并为您使用EF提供延迟执行

如果需要,您需要实现重载。

答案 1 :(得分:6)

它必须是一个字符串吗?为什么不创建一个将Func键选择器作为参数的方法。

public List<T> GetAllListOrdered<T,TProp>(SimpleOrderingDirectionEnum direction, Func<T,TProp> keySelector)
{
    return direction == SimpleOrderingDirectionEnum.Ascending ? _Repository.GetAllList().OrderBy(keySelector).ToList() : _Repository.GetAllList().OrderByDescending(keySelector).ToList();
}

然后将其称为

Func<ObjectToSortType, ObjectPropertyToSortBy> keySelector = r => r.Id;
GetAllListOrdered(SimpleOrderingDirectionEnum.Ascending, keySelector);

答案 2 :(得分:1)

如果Rob的答案对你来说还不够。试试Linq Dynamic。 http://dynamiclinq.azurewebsites.net/

using System.Linq.Dynamic; //Import the Dynamic LINQ library

//The standard way, which requires compile-time knowledge
//of the data model
var result = myQuery
    .Where(x => x.Field1 == "SomeValue")
    .Select(x => new { x.Field1, x.Field2 });

//The Dynamic LINQ way, which lets you do the same thing
//without knowing the data model before hand
var result = myQuery
    .Where("Field1=\"SomeValue\"")
    .Select("new (Field1, Field2)");

答案 3 :(得分:0)

感谢所有人。 Rob,你的解决方案与我最终的结果非常接近。

根据您的见解,我进行了更多搜索,并在Dynamic LINQ OrderBy on IEnumerable<T>(第二篇文章)中找到了Marc Gravel的答案。

它增加了动态作为额外奖励。