【问题标题】:How to set a value from an Expression for nested levels of depthness?如何从表达式中设置嵌套深度级别的值?
【发布时间】:2015-02-20 19:25:32
【问题描述】:

我的问题与以下两个问题非常相似,但我有一个额外的要求,这些要求不满足。

就像这些问题一样,我有一个Expression<Func<TEntity, TProperty>>,我想在其中为指定的属性设置一个值。如果表达式的主体只有一层深度,例如x => x.FirstName,那么这些解决方案效果很好,但如果主体更深,例如x => x.Parent.FirstName,它们就根本不起作用。

有什么方法可以获取这个更深层次的表达式并为其设置值吗?我不需要非常强大的延迟执行解决方案,但我确实需要可以在对象上执行的东西无论是 1 级还是多个级别,它都可以工作。我还需要支持您在数据库中期望的大多数典型类型(long、int?、string、Decimal、DateTime? 等,尽管我不关心更复杂的东西,比如地理类型)。

为了便于交流,假设我们正在处理这些对象,但假设我们需要处理 N 层深度,而不仅仅是 1 或 2:

public class Parent
{
    public string FirstName { get; set; }
}

public class Child
{
    public Child()
    {
        Mom = new Parent(); // so we don't have to worry about nulls
    }

    public string FavoriteToy { get; set; }
    public Parent Mom { get; set; }
}

假设这是我们的单元测试:

[TestFixture]
public class Tests
{
    [Test]
    public void MyTest()
    {
        var kid = new Child();
        Expression<Func<Child, string>> momNameSelector = (ch => ch.Mom.FirstName);
        Expression<Func<Child, string>> toyNameSelector = (ch => ch.FavoriteToy);

        kid.ExecuteMagicSetter(momNameSelector, "Jane");
        kid.ExecuteMagicSetter(toyNameSelector, "Bopp-It!");

        Assert.That(kid.Mom.FirstName, Is.EqualTo("Jane"));
        Assert.That(kid.FavoriteToy, Is.EqualTo("Bopp-It!"));
    }
}

我们正在研究的扩展方法(我没有设置它需要成为扩展方法,但它看起来很简单)看起来像这样:

public static TEntity ExecuteMagicSetter<TEntity, TProperty>(this TEntity obj, Expression<Func<TEntity, TProperty>> selector, TProperty value)
    where TEntity : class, new() // I don't require this but I can allow this restriction if it helps
{
    // magic
}

附:这个版本的代码是在 SO 编辑器中编写的——对于愚蠢的语法问题,我深表歉意,但这应该非常接近! #LockedDownWorkstationsSuck

【问题讨论】:

  • 成员表达式嵌套的深度无关紧要,只要表达式以属性/字段结尾即可。然后使用该表达式,您可以添加一个作业并完成。
  • @JeffMercado 我尝试过的这些问题的每个解决方案最终都会遇到使用反射调用Parent 对象(在我上面的示例中)上的setter方法但传入实例的问题Child 对象,因此在运行时存在类型不匹配而失败。我觉得缺少的魔法是递归或迭代地获取子属性对象,然后将它们传递给 setter 方法。
  • 那么请包括您对问题的尝试。您描述的问题听起来不像是与嵌套表达式有关的问题。如果您不是手动生成这些选择器表达式并让编译器处理它,那么它首先必须是一个有效的表达式。
  • 我尝试的解决方案是在我列出的问题中发布的与我的问题非常相似的解决方案。如果你愿意,我可以在这里复制那些,但我觉得那是多余的。这是你想要的吗?

标签: c# .net-4.0


【解决方案1】:

正如我在 cmets 中所说,它不应该那么复杂。使用选择器,只需向表达式添加一个赋值。您只需要编译并运行表达式。

public static TEntity ExecuteMagicSetter<TEntity, TProperty>(
        this TEntity obj,
        Expression<Func<TEntity, TProperty>> selector,
        TProperty value)
{
    var setterExpr = CreateSetter(selector);
    setterExpr.Compile()(obj, value);
    return obj;
}

private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>
        (Expression<Func<TEntity, TProperty>> selector)
{
    var valueParam = Expression.Parameter(typeof(TProperty));
    var body = Expression.Assign(selector.Body, valueParam);
    return Expression.Lambda<Action<TEntity, TProperty>>(body,
        selector.Parameters.Single(),
        valueParam);
}

【讨论】:

  • 对使用 lambda 表达式有点陌生。你能帮我使用你写的方法吗?
猜你喜欢
  • 1970-01-01
  • 2015-11-27
  • 2011-08-27
  • 1970-01-01
  • 2022-10-16
  • 1970-01-01
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多