C#谈到魔术串入lambda表达式 [英] C# Turning magic string into lambda expression
问题描述
我有一组扩展方法,允许在LINQ 的OrderBy()
方法使用魔法字符串。我所知道的第一个问题将是为什么,但它是一个通用存储库的一部分,是有灵活性,以便字符串可以从UI发送,直接使用。
I have a set of extension methods that allow for using magic strings in the LINQ OrderBy()
methods. I know the first question will be why, but it's part of a generic repository and is there for flexibility so that strings can be sent from the UI and used directly.
我有工作,如果你在一个神奇的字符串,表示你要查询的主要实体的财产通过,但我无法使它更加通用的,因此它可以处理多级魔渊字符串。例如:
I have it working if you pass in a magic string that represents a property on the main entity you are querying, but I'm having trouble making it more generic so it can handle multiple levels deep magic string. For example:
IQueryable<Contact> contacts = GetContacts();
contacts.OrderByProperty("Name"); // works great
// can't figure out how to handle this
contacts.OrderByProperty("ContactType.Name");
下面是我到目前为止的代码:
Here is the code that I have so far:
public static class LinqHelpers
{
private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2);
private static readonly MethodInfo ThenByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenBy" && method.GetParameters().Length == 2);
private static readonly MethodInfo ThenByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenByDescending" && method.GetParameters().Length == 2);
public static IOrderedQueryable<TSource> ApplyOrdering<TSource>(IQueryable<TSource> source, string propertyName, MethodInfo orderingMethod)
{
var parameter = Expression.Parameter(typeof(TSource), "x");
var orderByProperty = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda(orderByProperty, new[] { parameter });
var genericMethod = orderingMethod.MakeGenericMethod(new[] { typeof(TSource), orderByProperty.Type });
return (IOrderedQueryable<TSource>)genericMethod.Invoke(null, new object[] { source, lambda });
}
public static IOrderedQueryable<TSource> OrderByProperty<TSource>(this IQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, OrderByMethod);
}
public static IOrderedQueryable<TSource> OrderByDescendingProperty<TSource>(this IQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, OrderByDescendingMethod);
}
public static IOrderedQueryable<TSource> ThenByProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, ThenByMethod);
}
public static IOrderedQueryable<TSource> ThenByDescendingProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
{
return ApplyOrdering(source, propertyName, ThenByDescendingMethod);
}
}
我敢肯定,我需要拆分 propertyName的
的时间,然后用这些配件组装起来更复杂的表达,涉及 MemberExpression
再财产,但我挣扎。在正确的方向任何帮助或指示,将不胜感激。
I'm pretty sure I need to split the propertyName
on the period and then use those parts to build up a more complicated Expression that involves a MemberExpression
and then a Property but I'm struggling. Any help or pointing in the right direction would be appreciated.
推荐答案
我写我自己的谓词建设者类型的事情而回。我试图适应代码张贴在这里。这将返回一个表达式来访问属性,可以用来建立更复杂的表达式 - 只要确保表达的所有组件使用完全相同的参数
对象实例
I wrote my own predicate builder type thing a while back. I attempted to adapt the code for posting here. This returns an expression to access a property, and can be used to build up more complicated expressions - just make sure that all the components of the expression use the exact same param
object instance.
这不会在您的代码一滴工作。它会需要一些轻微的改动,使之成为您的使用,我认为工作
This won't work as a drop in for your code. It'll will require some slight adaptations to make it work for your use I think.
这输出参数=> (param.Child.IntProperty == 42)
。
您可以使用谓词
在where子句中的变量。比方说,你有一个列表<家长和GT;
名为家长
,你可以调用 parents.Where (谓语)
You could use the predicate
variable in a where clause. Let's say you had a List<Parent>
called parents
, you could call parents.Where(predicate)
.
public class Parent {
public string StringProperty { get; set; }
public Child Child { get; set; }
}
public class Child {
public int IntProperty { get; set; }
}
internal class Program {
private static void Main(string[] args) {
var param = Expression.Parameter(typeof(Parent), "param");
var accessExpression = GetAccessExpression(param, "Child.IntProperty", typeof(Parent));
var constantExpression = Expression.Constant(42);
var condition = Expression.Equal(accessExpression, constantExpression);
var predicate = Expression.Lambda<Func<Parent, bool>>(condition, param);
Console.WriteLine(predicate.ToString());
}
/// <summary>
/// Returns an Expression that represents member access for the specified property on the specified type. Uses recursion
/// to find the full expression.
/// </summary>
/// <param name="property">The property path.</param>
/// <param name="type">The type that contains the first part of the property path.</param>
/// <returns></returns>
private static Expression GetAccessExpression(Expression param, string property, Type type) {
if (property == null)
throw new ArgumentNullException("property");
if (type == null)
throw new ArgumentNullException("type");
string[] propPath = property.Split('.');
var propInfo = type.GetProperty(propPath[0]);
if (propInfo == null)
throw new Exception(String.Format("Could not find property '{0}' on type {1}.", propPath[0], type.FullName));
var propAccess = Expression.MakeMemberAccess(param, type.GetProperty(propPath[0]));
if (propPath.Length > 1)
return GetAccessExpression(propAccess, string.Join(".", propPath, 1, propPath.Length - 1), type.GetProperty(propPath[0]).PropertyType);
else
return propAccess;
}
}
这篇关于C#谈到魔术串入lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!