gpt4 book ai didi

c# - 将 MemberInitExpression 分配给属性的 ExpressionTree 方法

转载 作者:太空宇宙 更新时间:2023-11-03 23:20:15 27 4
gpt4 key购买 nike

我正在尝试使用表达式树,以便我可以选择使用 Entity Framework 映射到 DTO,其方式与 Include 指令在 DbSet(实现 OData 的开源项目的一部分)上的工作方式大致相同。

下面的代码代表一个测试用例。

Expression<Func<Bar, Bar>> mapBar = b => new Bar { BarInt = b.BarInt, BarString = b.BarString };
Expression<Func<Foo, Foo>> mapFoo = f => new Foo { FooInt = f.FooInt, B = null };

Expression<Func<Foo, Foo>> target = f => new Foo { FooInt = f.FooInt,
B = new Bar {
BarInt=f.B.BarInt, BarString = f.B.BarString
} };

注意 Foo.B 为空,并且插入了 mapBar 表达式。

我已经使用以下 ExpressionVisitor 添加了导航属性

public class UpdateExpressionVisitor : ExpressionVisitor
{
private readonly ParameterExpression _oldExpr;
private readonly Expression _newExpr;
public UpdateExpressionVisitor(ParameterExpression oldExpr, Expression newExpr)
{
_oldExpr = oldExpr;
_newExpr = newExpr;
}

protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
if (node.Member.Name == _oldExpr.Name)
{
return node.Update(_newExpr);
}
Console.WriteLine(node);
return base.VisitMemberAssignment(node);
}
}

但我不知道如何改变表达式 new Bar { BarInt = b.BarInt,...,成为 new Bar { BarInt = f.B.BarInt,...

该函数需要像这样的某种 ExpressionVisitor

public class MergingVisitor : ExpressionVisitor
{
private readonly ParameterExpression _oldExpr;
private readonly ParameterExpression _newProp;
private readonly ParameterExpression _newParent;
public MergingVisitor(ParameterExpression oldExpr, ParameterExpression newProp, ParameterExpression newParent)
{
_oldExpr = oldExpr;
_newProp = newProp;
_newParent = newParent;
}

protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression == _oldExpr)
{
/*!!what to do here!!*/
var ma = Expression.MakeMemberAccess(_newProp, node.Member);
return Expression.MakeMemberAccess(_newParent, ma.Member);
}
return base.VisitMember(node);
}

他们都需要用类似的东西链接在一起

public static Expression<Func<T, TMap>> MapNavProperty<T, TMap, U, UMap>(this Expression<Func<T, TMap>> parent, Expression<Func<U, UMap>> nav, string propName)
{
//concern 1 remap name of prop in nav - not sure if I should do this first
var parentInitVarName = parent.Parameters[0].Name;
var parentParam = Expression.Parameter(typeof(T), parentInitVarName);
var propParam = Expression.Parameter(typeof(U), propName);
var mergeVisitor = new MergingVisitor(nav.Parameters[0], propParam, parentParam);
var newNavBody = mergeVisitor.Visit(nav.Body); //as MemberExpression;

//concern 2 replace given property
var visitor = new UpdateExpressionVisitor(propParam, nav.Body);

return (Expression<Func<T, TMap>>)visitor.Visit(parent);
}

尽管目前 parentParam 将在 Expression.MakeMemberAccess 函数中失败,因为它是类型 T(上例中的 Foo),而不是类型 U。

如何更改子属性的 lamda 表达式中的变量名称和类型 - 谢谢。

更新

Svick 和 Ivan Stoev 的答案在解释上博学多才,而且都工作得很好——(通过的)单元测试和来自两个答案的代码都在 on GitHub here .非常感谢你们 - 很遗憾我不能勾选 2 个答案,所以归结为 Eeny,meeny,miny,moe。

最佳答案

var parentParam = Expression.Parameter(typeof(T), parentInitVarName);

这行不通,表达式中的参数不是通过名称来标识的,而是通过引用来标识的。您需要做的只是获取父级的参数。


var propParam = Expression.Parameter(typeof(U), propName);

这对我来说没有任何意义。 propName是一个属性的名称,所以你需要用它来创建成员访问表达式,而不是参数表达式。


public MergingVisitor(ParameterExpression oldExpr, ParameterExpression newProp, ParameterExpression newParent)

您需要用另一个表达式 (f.B) 替换一个表达式 (b)。为此,我将创建:

public class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression _oldExpr;
private readonly Expression _newExpr;

public ReplaceVisitor(Expression oldExpr, Expression newExpr)
{
_oldExpr = oldExpr;
_newExpr = newExpr;
}

public override Expression Visit(Expression node)
{
if (node == _oldExpr)
{
return _newExpr;
}
return base.Visit(node);
}
}

public UpdateExpressionVisitor(ParameterExpression oldExpr, Expression newExpr)

oldExpr 是参数表达式没有任何意义。您需要的只是一个字符串


var visitor = new UpdateExpressionVisitor(propParam, nav.Body);

您需要在此处实际使用 newNavBody


整个 MapNavProperty() 现在看起来像这样:

var parentParam = parent.Parameters.Single();
var propExpression = Expression.Property(parentParam, propName);
var mergeVisitor = new ReplaceVisitor(nav.Parameters.Single(), propExpression);
var newNavBody = mergeVisitor.Visit(nav.Body);

var visitor = new UpdateExpressionVisitor(propName, newNavBody);

return (Expression<Func<T, TMap>>)visitor.Visit(parent);

通过这些更改,您的代码将正常工作。

关于c# - 将 MemberInitExpression 分配给属性的 ExpressionTree 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35681045/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com