2014-12-22 52 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

剛剛使用lambda表達式。你能幫助我消耗你寫的方法嗎? – user203687