2014-12-22 2 views
3

Мой вопрос очень похож на следующие два вопроса, но у меня есть дополнительное требование, которое они не удовлетворяют.Как установить значение из выражения для вложенных уровней глубины?

Так же, как эти вопросы, у меня есть 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

Не имеет значения, насколько глубоко вложены ваши выражения-члены, до тех пор, пока выражение заканчивается в свойстве/поле. Затем с этим выражением вы можете добавить задание, и все готово. –

+0

@JeffMercado Каждое решение из тех вопросов, которые я попытался, в конечном итоге имеет проблему использования отражения для вызова метода setter (в моем примере выше) объекта Parent, но с переданным в экземпляре объектом 'Child', так что во время выполнения есть несоответствие типа, которое терпит неудачу. Я чувствую, что недостающая магия либо рекурсивно, либо итеративно получает объекты дочернего объекта, а затем передает их методу setter. – Jaxidian

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

Бит новый для использования лямбда-выражений. Можете ли вы помочь мне в использовании методов, которые вы написали. – user203687

Смежные вопросы