我的问题与以下两个问题非常相似,但我有一个额外的要求,即这些问题不满足。
就像那些问题一样,我有一个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
}
P.S。这个版本的代码是在SO编辑器中编写的 - 我为愚蠢的语法问题道歉,但这应该是非常接近的! #LockedDownWorkstationsSuck
答案 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);
}