C# 如何使用表达式<;Func<;T>&燃气轮机;在linq语句中,如果我的导航属性是单个项?

C# 如何使用表达式<;Func<;T>&燃气轮机;在linq语句中,如果我的导航属性是单个项?,c#,linq,C#,Linq,我有一个表达式,它将数据库对象从另一个系统映射到我可以在任何系统中使用的标准对象 例: 私有静态表达式位置映射= l=>新的MyLocation() { Id=l.unique\u Id, 代码=l.位置\ U代码, 主动的 ……其他财产 } 这与我在以下情况下的预期完全一样 public IEnumerable<MyLocation> GetActiveLocations() { return otherSystem.location_ta

我有一个表达式,它将数据库对象从另一个系统映射到我可以在任何系统中使用的标准对象

例:

私有静态表达式位置映射=
l=>新的MyLocation()
{
Id=l.unique\u Id,
代码=l.位置\ U代码,
主动的
……其他财产
}
这与我在以下情况下的预期完全一样

public IEnumerable<MyLocation> GetActiveLocations()
        {
            return otherSystem.location_table
                .Select(LocationMap)
                .Where(l => l.Active == true)
                .ToList();
        }
public IEnumerable GetActiveLocations()
{
返回otherSystem.location\u表
.选择(位置地图)
.Where(l=>l.Active==true)
.ToList();
}
但我似乎找不出一个办法让它成为另一个表达的一部分

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
        };
private静态表达式UserMap=
e=>newuser()
{
Id=e.unique\u Id,
FirstName=e.fname,
LastName=e.lname,
位置=e.过度位置
.Select(LocationMap)//将无法工作,因为
//e.U位置不是集合
};

我知道我可以把它编译成一个函数,但是它必须为成千上万的用户执行。什么是使这项工作正常进行的正确方法?

因此,我们需要两种方法。一个是将一个表达式与另一个表达式组合在一起,即使用一个表达式计算值,另一个表达式获取该输出并计算新值,然后创建一个新表达式,该表达式使用两个表达式获取第一个表达式的输入并生成第二个表达式的输出

我们将首先创建一个表达式以从用户处获取位置:

Expression<Func<excessive_user, excessive_location>> locationProjection = 
    user => user.excessive_location;
接下来,我们需要一个非常相似但略有不同的方法,可以将两个表达式组合在一起,能够获取一个表达式,另一个可以获取相同的输入和原始表达式的输出,并生成新的输出

此方法允许我们编写以下内容:

mappedLocationProject.Combine((user, location) => new User()
        {
            Id = user.unique_id,
            FirstName = user.fname,
            LastName = user.lname,
            Location = location
        };
下面是合成表达式的方法:

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);
}
它们都使用以下方法将一个表达式的所有实例替换为另一个:

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);
}

为了实现这个目标,您需要一些表达式助手

首先让我向您展示生成的代码:

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));

您是否担心每次编译时对性能的影响,还是试图将它们保留为表达式,以便将它们转换为SQL(解决SELECT N+1问题)?我希望将其全部编译成一个SQL语句,专门解决每次迭代运行函数时对性能的影响。我希望我正确地理解了你的问题以及使用表达式的整个想法。我对表达式树和访问者不是很有经验,所以我不知道这到底在做什么,但我还是把它全部插入了。好消息是它提供了我期望的输出,但坏消息是它比我使用LocationMap的AllLocations方法要慢得多,只是在EmployeeMap表达式中重写了相同的映射。我认为是10倍。你知道我该如何加快速度,使其具有可比性吗?也许是一种保存组合/组合表达式以供重用的方法?@user3893011只需将表达式保存在变量中,就像保存任何其他要存储的计算值一样。在这方面,表达方式与其他任何表达方式都没有区别。
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);
}
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);
}
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));
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);
        }
    }
}