简体   繁体   English

使用 Expression.Lambda 制作 lambda 表达式时如何使用变量<func<> &gt;() </func<>

[英]How to use a variable when making a lambda expression using Expression.Lambda<Func<>>()

I have a linq query which is as follows:我有一个 linq 查询,如下所示:

var query = _appDbContext.Persons
                .Where(p => p.Id == 123)
                .Select(lambda)
                .ToList();

I use lambda because i have a variable in my select statement.我使用 lambda 因为我的 select 语句中有一个变量。 The lambda: lambda:

var wantedData = "Adress";

var x = Expression.Parameter(typeof(Person), "x");
var body = Expression.PropertyOrField(x, wantedData);
var lambda = Expression.Lambda<Func<Person, Adress>>(body, x);

The query will return the addresses for person with id 123. Let's say instead of the addresses I would like to recieve all subscriptions this person has.该查询将返回 id 为 123 的人的地址。假设我希望接收此人的所有订阅,而不是地址。 Only setting wantedData = "Subscription" will not work, because of the "Adress" in the lambda statement.由于 lambda 语句中的“地址”,仅设置wantedData = "Subscription"将不起作用。 So I'm looking for a way to use a variable to change "Adress" in the lambda statement.所以我正在寻找一种方法来使用变量来更改 lambda 语句中的“地址”。

I tried the following, which obviously does not work.我尝试了以下方法,这显然不起作用。

var lambda = Expression.Lambda<Func<Person, wantedData>>(body, x);

Is there a way to do this?有没有办法做到这一点?

Ok, I managed to create a generic query method that takes an Expression as parameter.好的,我设法创建了一个以表达式为参数的通用查询方法。

EDIT: This will receive a string value and will try to match it with an existing property on your Person class.编辑:这将收到一个字符串值,并将尝试将其与您的Person class 上的现有属性匹配。

private IEnumerable<TResult> GetPersonData<TResult>(Expression<Func<Person, TResult>> selectExpression)
{
    return _dbContext.Persons
            // .Where(filter)
        .Select(selectExpression)
        .ToList();
}

public IEnumerable<object> GetData(string dataPropertyName)
{
    switch(dataPropertyName) {
         case nameof(Person.Address): return GetPersonData(p => p.Address);
         case nameof(Person.Subscription): return GetPersonData(p => p.Subscription);
         // other cases
         default: throw new InvalidArgumentException("Invalid property name");
    }
}

note that this code is just an example written on the spot and it might not work directly with copy-paste请注意,此代码只是当场编写的示例,它可能无法直接用于复制粘贴

I've made something similar to an OrderBy and tweak it a little for your select.我做了一些类似于OrderBy的东西,并为你的 select 稍微调整了一下。 This only works if you know the return type.这仅在您知道返回类型时才有效。 You can create an extension method with the following code:您可以使用以下代码创建扩展方法:

public static IQueryable<TResult> Select<T,TResult>(this IQueryable<T> source, string propertyName)
{
    var prop = typeof(T).GetProperties()
        .FirstOrDefault(x => x.Name.ToUpper() == propertyName.ToUpper());
    ParameterExpression pe = Expression.Parameter(typeof(T), "x");
    MemberExpression body = Expression.Property(pe, prop.Name);

    LambdaExpression lambda = Expression.Lambda(body, new ParameterExpression[] { pe });

    MethodCallExpression selectCallExpression = Expression.Call(
        typeof(Queryable),
        "Select",
        new Type[] { source.ElementType, prop.PropertyType },
        source.Expression,
        lambda);
    return source.Provider.CreateQuery<TResult>(selectCallExpression);
}

That way, you can use it like:这样,您可以像这样使用它:

var query = _appDbContext.Persons
                .Where(p => p.Id == 123)
                .Select<Person,string>(wantedData)
                .ToList();

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

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