如何在运行时选择OrderBy / Where的属性(不使用字符串参数)?

时间:2012-09-19 16:41:14

标签: c# linq linq-to-entities expression-trees

更新示例以显示更多一般用法。

我有一个允许用户提供本地化的实体:

public class ResourceValue
{
    public int ResourceValueId { get; set; }
    public string EnglishValue { get; set; }
    public string FrenchValue { get; set; }
    public string SpanishValue { get; set; }
    etc...
}

用于许多其他实体:

public class SomeEntity
{
    public int Id { get; set; }
    public virtual ResourceValue Name { get; set; }
    public virtual ResourceValue ShortDescription { get; set; }
    public virtual ResourceValue LongDescription { get; set; }
    etc...
}

我想做这样的事情:

return context.SomeEntities.OrderBy(x => x.Name);

让这项工作好像我已经这样做了:

return context.SomeEntities.OrderBy(x => x.Name.FrenchValue);

基于CurrentUICulture为“fr-CA”。

我一直在尝试基于Marc Gravell在这里回答的一些事情:https://stackoverflow.com/a/1231941但是却未能得到我想要的东西。

更新 - 这非常接近,但我宁愿将其命名为“OrderBy”,因此终结编码器可以使用它而无需特别考虑:

    public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
    {
        return ApplyLocalizedOrder(source, keySelector, "OrderBy");
    }

    public static IOrderedQueryable<TSource> ApplyLocalizedOrder<TSource, TKey>(IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, string methodName)
    {
        ParameterExpression arg = keySelector.Parameters[0];
        Expression expr = Expression.PropertyOrField(keySelector.Body, GetCurrentCulture());
        LambdaExpression lambda = Expression.Lambda<Func<TSource, string>>(expr, arg);

        return (IOrderedQueryable<TSource>)typeof(Queryable).GetMethods().Single(
                method => method.Name == methodName
                        && method.IsGenericMethodDefinition
                        && method.GetGenericArguments().Length == 2
                        && method.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(TSource), expr.Type)
                .Invoke(null, new object[] { source, lambda });
    }

2 个答案:

答案 0 :(得分:0)

虽然动态创建lambda表达式很酷,但只需创建一个在查询之上应用排序的方法,就可以以更简单的方式实现结果。该方法看起来像这样:

    private static IQueryable<SomeEntity> OrderByName(IQueryable<SomeEntity> source, string culture)
    {
        if (culture == "fr-CA")
        {
            return source.OrderBy(x => x.Name.FrenchValue);
        }

        return source.OrderBy(x => x.Name.EnglishValue);
    }

然后你会按如下方式使用它:

OrderByName(context.SomeEntities, "en-US")

以下是整个例子:

public class MyCtx1 : DbContext
{
    public DbSet<SomeEntity> SomeEntities { get; set; }
    public DbSet<ResourceValue> ResourceValues { get; set; }
}

public class SomeEntity
{
    public int Id { get; set; }
    public virtual ResourceValue Name { get; set; }
}

public class ResourceValue
{
    public int ResourceValueId { get; set; }
    public string EnglishValue { get; set; }
    public string FrenchValue { get; set; }
}

class Program
{

    private static IQueryable<SomeEntity> OrderByName(IQueryable<SomeEntity> source, string culture)
    {
        if (culture == "fr-CA")
        {
            return source.OrderBy(x => x.Name.FrenchValue);
        }

        return source.OrderBy(x => x.Name.EnglishValue);
    }

    static void Main(string[] args)
    {
        using (var context = new MyCtx1())
        {
            if (!context.SomeEntities.Any())
            {
                context.SomeEntities.Add(
                    new SomeEntity() 
                    { 
                        Name = new ResourceValue()
                        {
                            EnglishValue = "abc - en",
                            FrenchValue = "xyz - fr"
                        }
                    });

                context.SomeEntities.Add(
                    new SomeEntity() 
                    { 
                        Name = new ResourceValue()
                        {
                            EnglishValue = "xyz - en",
                            FrenchValue = "abc - fr"
                        }
                    });

                context.SaveChanges();
            }

            Console.WriteLine("Ordered by english name");
            DisplayResults(OrderByName(context.SomeEntities, "en-US"));

            Console.WriteLine("Ordered by french name");
            DisplayResults(OrderByName(context.SomeEntities, "fr-CA"));
        }
    }

    private static void DisplayResults(IQueryable<SomeEntity> q)
    {
        foreach (var e in q)
        {
            Console.WriteLine(e.Id);
        }                
    }

结果:

Ordered by english name
1
2
Ordered by french name
2
1
Press any key to continue . . .

答案 1 :(得分:0)

context.SomeEntities.Select(v => v.Name.FrenchName).OrderBy(x => x);

但是,甚至比get中的名称更好,返回当前文化或某种文化或代码所要求的任何内容,当您的课程可以在Linq查询中执行时,没有理由这样做。当它在课堂上完成时无论如何都会更好,因为那时你调用代码的任何地方都会返回正确的文化。