如何从表达式中为嵌套深度级别设置值?

Jax*_*ian 5 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; }
}
Run Code Online (Sandbox Code Playgroud)

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

[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!"));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

PS:此版本的代码是在SO编辑器中编写的-对于抱歉的语法问题,我深表歉意,但请不要靠近!#LockedDownWorkstationsSuck

Jef*_*ado 5

正如我在评论中指出的那样,它不应该那么复杂。使用选择器,只需向表达式添加一个赋值即可。您只需要编译并运行表达式。

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);
}
Run Code Online (Sandbox Code Playgroud)