如何为Expression的嵌套深度级别设置一个值?

时间:2014-12-22 16:57:39

标签: c# .net-4.0

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

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

有没有办法采用这种更深层的表达方式并为其设置值?我不需要一个非常强大的执行延迟解决方案,但我确实需要一些我可以执行的一个对象,无论是1级还是多级都可以工作。我还需要支持您在数据库中预期的大多数典型类型(longint?stringDecimalDateTime?等。虽然我不关心地理类型等更复杂的事情。

为了对话,我们假设我们正在使用这些对象,但我们假设我们需要处理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
}

P.S。这个版本的代码是在SO编辑器中编写的 - 我为愚蠢的语法问题道歉,但这应该是非常接近的! #LockedDownWorkstationsSuck

1 个答案:

答案 0 :(得分:4)

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

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