2012-07-13 88 views
8

類似:Convert a string to Linq.Expressions or use a string as Selector?如何將一個字符串轉換爲linq表達式?

類似的一個之一:Passing a Linq expression as a string?

用相同的回答另一個問題:How to create dynamic lambda based Linq expression from a string in C#?

原因,詢問一些東西,有這麼多的類似的問題:

接受的在這些類似的問題中回答是不可接受的,因爲它們都參考了一個4年前的圖書館(假設它是由代碼大師Scott Gu編寫的)爲舊框架(.net 3.5)編寫的,並且不提供作爲答案,除了鏈接以外,什麼都不願意。

有一種方法可以在不包含整個庫的情況下在代碼中執行此操作。

下面是這種情況的一些示例代碼:

public static void getDynamic<T>(int startingId) where T : class 
    { 
     string classType = typeof(T).ToString(); 
     string classTypeId = classType + "Id"; 
     using (var repo = new Repository<T>()) 
     { 
      Build<T>(
      repo.getList(), 
      b => b.classTypeId //doesn't compile, this is the heart of the issue 
       //How can a string be used in this fashion to access a property in b? 
      ) 
     } 
    } 

    public void Build<T>(
     List<T> items, 
     Func<T, int> value) where T : class 
    { 
     var Values = new List<Item>(); 
     Values = items.Select(f => new Item() 
     { 
      Id = value(f) 
     }).ToList(); 
    } 

    public class Item 
    { 
    public int Id { get; set; } 
    } 

注意,這並不打算把整個字符串轉換成表達式,比如

query = "x => x.id == somevalue"; 

但取而代之的是試圖只使用字符串作爲訪問

query = x => x.STRING; 
+0

你有沒有排除了做簡單的反射讓你的'Func'? – 2012-07-13 23:41:11

+0

@PaulPhillips - Reflection將產生傳入的類T中屬性的屬性類型或名稱。當連接到實體框架存儲庫時,類T僅作爲類型的引用傳遞。從存儲庫('repo.getList()')返回所有類T的列表。反射如何幫助包含linq表達式中類T的屬性? – 2012-07-13 23:45:47

+0

您的示例使它看起來像是在執行linq-to-objects,在這種情況下,您可以反射性地獲取該屬性的值。但既然你在另一個環境下工作,我不確定它會起作用。我發佈了我的嘗試,所以你可以檢查 – 2012-07-13 23:47:09

回答

15

這裏是一個表達式樹的嘗試。我仍然不知道這是否適用於Entity框架,但我認爲這是值得一試的。

Func<T, int> MakeGetter<T>(string propertyName) 
{ 
    ParameterExpression input = Expression.Parameter(typeof(T)); 

    var expr = Expression.Property(input, typeof(T).GetProperty(propertyName)); 

    return Expression.Lambda<Func<T, int>>(expr, input).Compile(); 
} 

這樣稱呼它:

Build<T>(repo.getList(), MakeGetter<T>(classTypeId)) 

如果你能在地方的只是一個Func使用Expression<Func<T,int>>,則只是刪除調用Compile(和變化MakeGetter簽名)。


編輯: 在評論,TravisJ問他怎麼可以這樣使用它:w => "text" + w.classTypeId

有幾種方法可以做到這一點,但爲便於閱讀,我會建議先引入一個局部變量,像這樣:

var getId = MakeGetter<T>(classTypeId); 

return w => "text" + getId(w); 

重點是,getter只是一個函數,你可以像你一樣使用它。閱讀Func<T,int>這樣的:int DoSomething(T instance)

+0

我能夠得到這個在linqPad工作 - 這裏是代碼https://gist.github.com/3108507 – Hogan 2012-07-14 00:44:43

+0

@Paul - 感謝您的一些工作代碼和示例。我也能夠得到這個工作。我沒有改變簽名,因爲它會干擾更多的線路。我確實有一個問題,我需要做什麼才能以這種方式使用'MakeGetter':'w =>「text」+ MakeGetter (classTypeId)'?類似於'w =>「text」+ w.classTypeId'。 – 2012-07-15 19:23:08

+0

@TravisJ查看我的編輯 – 2012-07-16 02:53:04

1

我還沒有試過這個,不知道它是否會工作,但你可以使用類似:

b => b.GetType().GetProperty(classTypeId).GetValue(b, null);

+0

這看起來很有趣,至少可以編譯。仍然有更多的測試要做。 – 2012-07-13 23:57:01

2

這是給你的擴展方法與我的測試代碼(linqPad):

class test 
{ 
    public string sam { get; set; } 
    public string notsam {get; set; } 
} 

void Main() 
{ 
    var z = new test { sam = "sam", notsam = "alex" }; 

    z.Dump(); 

    z.GetPropertyByString("notsam").Dump(); 

    z.SetPropertyByString("sam","john"); 

    z.Dump(); 
} 

static class Nice 
{ 
    public static void SetPropertyByString(this object x, string p,object value) 
    { 
    x.GetType().GetProperty(p).SetValue(x,value,null); 
    } 

    public static object GetPropertyByString(this object x,string p) 
    { 
    return x.GetType().GetProperty(p).GetValue(x,null); 
    } 
} 

結果:

results

+0

這在你的例子中有效,但是,我在這裏看不到與linq的集成。請參閱Paul對linq集成的回答。 – 2012-07-15 19:24:19

+0

我想,他的作品在一個類型的礦上工作的對象... 6之一。因爲沒有模板需求,所以我認爲我的表現會更好。 – Hogan 2012-07-15 19:30:59