C# 如何在运行时为OrderBy/Where选择属性(不使用字符串参数)?
更新示例以显示更一般的用法 我有一个允许用户提供本地化的实体:C# 如何在运行时为OrderBy/Where选择属性(不使用字符串参数)?,c#,linq,linq-to-entities,expression-trees,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 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);
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);
}
我想这样做:
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);
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);
}
把这项工作当作我做过的:
return context.SomeEntities.OrderBy(x => x.Name.FrenchValue);
基于当前的UICulture为“fr CA”
根据马克·格雷威尔的回答,我一直在尝试一些事情:但没能得到我想要的
更新-这非常接近,但我更希望将其命名为“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 });
}
public static IOrderedQueryable OrderBy(此IQueryable源、表达式keySelector)
{
返回ApplyLocalizedOrder(源、键选择器、“OrderBy”);
}
公共静态IOrderedQueryable ApplyLocalizedOrder(IQueryable源、表达式键选择器、字符串方法名)
{
ParameterExpression arg=keySelector.Parameters[0];
Expression expr=Expression.PropertyOrField(keySelector.Body,GetCurrentCulture());
LambdaExpression lambda=Expression.lambda(expr,arg);
return(IOrderedQueryable)typeof(Queryable).GetMethods().Single(
method=>method.Name==methodName
&&方法.IsGenericMethodDefinition
&&方法。GetGenericArguments()。长度==2
&&方法。GetParameters().Length==2)
.MakeGenericMethod(typeof(TSource),expr.Type)
.Invoke(null,新对象[]{source,lambda});
}
虽然动态创建lambda表达式很酷,但只需创建一个将排序应用于查询顶部的方法,就可以以更简单的方式获得结果。方法如下所示:
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);
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);
}
以下是整个示例:
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);
}
}
但是,甚至比在
get
for name中更好的是,返回当前区域性或某个区域性或代码调用的任何内容,当您的类可以这样做时,没有理由在Linq查询中这样做。当它在类中完成时,无论如何都会更好,因为无论在哪里调用代码,它都会返回正确的区域性。在这种特定情况下,这会起作用,但我希望在许多实体的几十个字段上使用ResourceValue。例如,某些实体可能还具有可本地化的ShortDescription、LongDescription等。