简体   繁体   English

我如何使用表达式 <Func<T> &gt;在linq语句中,如果我的导航属性是单个项目?

[英]How do i use an Expression<Func<T>> in a linq statement if my navigation property is a single item?

I have an expression that maps the database object from another system to a standard object that I can use across any system. 我有一个表达式,它将数据库对象从另一个系统映射到我可以在任何系统上使用的标准对象。

Ex: 例如:

private static Expression<Func<excessive_location, MyLocation>> LocationMap =
    l => new MyLocation()
        {
            Id = l.unique_id,
            Code = l.location_code,
            Active = l.active

            ...Other properties
        }

This works exactly as I expect in the following situation 在以下情况下,这完全符合我的预期

public IEnumerable<MyLocation> GetActiveLocations()
        {
            return otherSystem.location_table
                .Select(LocationMap)
                .Where(l => l.Active == true)
                .ToList();
        }

But I cant seem to figue out a way to make it work as part of another expression 但我似乎无法想出一种使其成为另一种表达方式的一部分的方法

private static Expression<Func<excessive_user, User>> UserMap =
    e => new User()
        {
            Id = e.unique_id,
            FirstName = e.fname,
            LastName = e.lname,
            Location = e.excessive_location
                .Select(LocationMap)  // will not work since
                                      // e.excessive_location is not a collection
        };

I know I can compile it to a function but then it will have to execute for every user out of thousands. 我知道我可以将其编译为函数,但随后必须为数千个用户中的每个用户执行。 What is the proper way to make this work? 使这项工作正确的方法是什么?

In order to achieve the goal, you'll need some expression helpers. 为了实现该目标,您将需要一些表达帮助器。

Let me first show you the resulting code: 首先让我向您展示结果代码:

private static Expression<Func<excessive_location, MyLocation>> LocationMap =
    l => new MyLocation()
    {
        Id = l.unique_id,
        Code = l.location_code,
        Active = l.active
    };

private static Expression<Func<excessive_user, User>> UserMap =
    Utils.Expr((excessive_user u) => new User
    {
        Id = u.unique_id,
        FirstName = u.fname,
        LastName = u.lname
    })
    .BindMemberInit(u => u.Location, 
        Utils.Expr((excessive_user u) => u.excessive_location).Bind(LocationMap));

Now the helpers. 现在是助手。

The first one called Expr is a simple method that allows you to define expression without local variable. 第一个称为Expr的方法很简单,它允许您定义不带局部变量的表达式。

The second one called Bind allows you to bind expression to another expression accessor. 第二个Bind称为Bind允许您将表达式绑定到另一个表达式访问器。

So having 所以有

(excessive_user u) => u.excessive_location

and

(excessive_location l) => new MyLocation { Id = l.unique_id, .... }

you can make 你(们)能做到

(excessive_user u) => new MyLocation { Id = u.excessive_location.inique_id, ...}

The third one called BindMemberInit allows you to add new member initialization to the existing new { ... } expression. 第三个名为BindMemberInit允许您将新成员初始化添加到现有的new { ... }表达式中。

The last one called ReplaceParameter allows you to replace expression parameter with another expression. 最后一个称为ReplaceParameter表达式使您可以将表达式参数替换为另一个表达式。

Here is the full code: 这是完整的代码:

public static class Utils
{
    public static Expression<Func<T, TResult>> Expr<T, TResult>(Expression<Func<T, TResult>> e) { return e; }

    public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
    {
        var body = resultSelector.Body.ReplaceParameter(resultSelector.Parameters[0], source.Body);
        return Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
    }

    public static Expression<Func<TSource, TTarget>> BindMemberInit<TSource, TTarget, TMember, TValue>(this Expression<Func<TSource, TTarget>> expression, Expression<Func<TTarget, TMember>> member, Expression<Func<TSource, TValue>> value)
    {
        var binding = Expression.Bind(
            ((MemberExpression)member.Body).Member,
            value.Body.ReplaceParameter(value.Parameters[0], expression.Parameters[0]));
        var body = (MemberInitExpression)expression.Body;
        return expression.Update(body.Update(body.NewExpression, body.Bindings.Concat(new[] { binding })), expression.Parameters);
    }

    static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
    {
        return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ParameterReplacer : ExpressionVisitor
    {
        public ParameterExpression Source;
        public Expression Target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == Source ? Target : base.VisitParameter(node);
        }
    }
}

So what we need here is a two methods. 因此,我们这里需要两种方法。 One to compose one expression with another, that is to take an expression that computes a value, another expression that takes that output and computes a new value, and creates a new expression that takes the input of the first and produces the output of the second, using both expressions. 一个将一个表达式与另一个表达式组合,即采用一个计算值的表达式,另一个采用该输出结果并计算新值的表达式,并创建一个采用第一个输入并产生第二个输出的新表达式。 ,同时使用两个表达式。

We'll start by creating an expression to get the location from a user: 我们将首先创建一个表达式以从用户那里获取位置:

Expression<Func<excessive_user, excessive_location>> locationProjection = 
    user => user.excessive_location;

Then we'll compose that with your expression to turn the location into a MyLocation : 然后,将其与您的表达式组合起来,以将该位置转换为MyLocation

Expression<Func<excessive_user, MyLocation>> mappedLocationProject = 
    locationProjection.Compose(LoactionMap);

Next we'll need a very similar, but slightly different method that can combine two expressions together, being able to take an expression, and another that takes the same input, and the output of the original, and produces a new output. 接下来,我们将需要一个非常相似但略有不同的方法,该方法可以将两个表达式组合在一起,能够使用一个表达式,另一个方法则使用相同的输入和原始输出,并产生新的输出。

This method would allow us to write the following: 该方法使我们可以编写以下内容:

mappedLocationProject.Combine((user, location) => new User()
        {
            Id = user.unique_id,
            FirstName = user.fname,
            LastName = user.lname,
            Location = location
        };

Here's the method to compose expressions: 这是组成表达式的方法:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

And here's the implementation of Combine : 这是Combine的实现:

public static Expression<Func<TFirstParam, TResult>>
    Combine<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], param)
        .Replace(second.Parameters[1], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

They both use the following method to replace all instances of one expression with another: 它们都使用以下方法将一个表达式的所有实例替换为另一个:

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}
public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

暂无
暂无

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

相关问题 如何重构LINQ查询以使用委托或表达式 <Func<T, bool> &gt; - How to Refactor LINQ Query to Use Delegate or Expression<Func<T, bool>> 如何提取传递给Expression的属性名称和值 <Func<T,bool> &gt;? - How do I extract the property name and value being passed into an Expression<Func<T,bool>>? 我如何模仿IQueryable的功能 <T> .INCLUDE(表达式 <Func<T,TProperty> &gt;路径)在我的自定义集合中? - How do I mimic the functionality of IQueryable<T>.Include(Expression<Func<T,TProperty>> path) in my custom collection? 如何在Linq to Entity Framework的表达式中使用Func? - How to use a Func in an expression with Linq to Entity Framework? 如何将 Linq 中的 func 用于实体 select? - How do I use func in Linq to Entity select? 如何使用Func <Tkey,T> 在linq? - How can I use Func<Tkey,T> in linq? 如何合并表达式<func<t, tpropertytype> &gt; 进入我的 IQueryable? </func<t,> - How can I incorporate an Expression<Func<T, TPropertyType>> into my IQueryable? 如何使用表达式 <Func> 设置嵌套属性? - How to use an Expression<Func> to set a nested property? 我可以使用Func / Predicate或Linq表达式创建列表的通用过滤 <T> ? - Can I use Func/Predicate or Linq expression to create generic filtering of a List<T>? 我可以动态创建一个表达式 <Func<T, bool> &gt;谓词,但我如何创建表达式 <Func<T1,T2,bool> &gt; - I can dynamically create an Expression<Func<T, bool>> predicate ,but how do i create Expression<Func<T1,T2,bool>>
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM