我的问题与以下两个问题非常相似,但是我有一个附加要求,即这些问题不能满足。
就像那些问题一样,我有一个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; }
}
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
正如我在评论中指出的那样,它不应该那么复杂。使用选择器,只需向表达式添加一个赋值即可。您只需要编译并运行表达式。
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)