2012-11-19 58 views
0

給定Expression<Func<T, TValue>>(如m => m.Name)和index,我希望能夠將我的表達式轉換爲m => m[index].Name)。我必須承認我被卡住了......將表達式m => m.Name轉換爲m => m [index]。名稱

如果你想要「爲什麼地獄」(也許找到更好的方法),我給你的實際情況。

場景:想象一下服務器端可編輯網格沒有javascript)。

我建立我與助手電網看起來像:

@(Html.Grid(Model) 
.Columns(columns => { 
    columns.Edit(m => m.Name); 
    columns.Edit(m => m.Code); 
}) 
.AsEditable()); 

模型是IQueryable<T>

m => m.NameExpression<Func<T, TValue>>TValue的字符串)

m => m.CodeExpression<Func<T, TValue>>TValue是int)

當rende響我的看法,我想顯示一個HTML表單。 列舉了IQueryable<T>(命令,分頁)。 => ok

所以我會有一個List<T>的5,10或20 T項目。

而且NameCode應使用經典HtmlHelper.TextBoxFor(Expression<Func<T, TValue>>)(沒有問題創造了HtmlHelper<T>

但正如我已經有了一個名單被表示爲TextBox,如果我想正確的模型結合,我不能直接使用m => m.Name,但應該使用m => m[indexOfItem in List<T>].Name

編輯:更多細節:

所以我們可以說,我們有一個實體類

public class Test { 
    public int Id {get;set;} 
    public string Name {get;set;} 
    public string Code {get;set;} 
} 

然後,檢索一個IQueryable<Test>

然後給定的參數的圖

@model IQueryable<Test> 

@(Html.Grid(Model) 
    .Columns(columns => { 
     columns.Edit(m => m.Name); 
     columns.Edit(m => m.Code); 
    }) 
    .AsEditable()); 

你看,模型的方法是IQueryable<Test>

m => m.Name 

m => m.Code 

只是模型的屬性(我想顯示爲文本框在我的網格)。

該模型是IQueryable<T>(而不是IEnumerable<T>),因爲網格管理排序和分頁,因此我的控制器和服務層不需要知道分頁和排序。

更清楚了嗎?

+0

您所使用的拉姆達的做法是正確的。鑑於列舉元素並渲染文本框。你想在哪裏「得到」indexOfItem? –

+0

當爲我的網格的每一行調用HtmlHelper.TextBoxFor時。如果我使用Html.TextBoxFor(m => m.Code),它會生成''。但是我需要爲每一行指定一個獨特的ID和名稱,否則我的模型綁定將失敗(Post操作應該得到一個'List '作爲參數。我可以手動創建'input',但是尋找更優雅的方式。 .. –

+0

如果你有索引,爲什麼不直接寫'columns.Edit(m => m [index] .Name);'?實際上問題是錯誤的,你不能變換'm => m.Name'進入'm => m [index] .Name'只是因爲'm'在這個上下文中並不是一個集合,所以我想你需要一個不同的轉換,但是我不太明白哪個。 –

回答

1

這可以通過編寫自定義ExpressionVisitor可以輕鬆實現:

public static class ExpressionExtensions 
{ 
    private class Visitor : ExpressionVisitor 
    { 
     private readonly int _index; 
     public Visitor(int index) 
     { 
      _index = index; 
     } 

     protected override Expression VisitParameter(ParameterExpression node) 
     { 
      return Expression.ArrayIndex(GetArrayParameter(node), Expression.Constant(_index)); 
     } 

     public ParameterExpression GetArrayParameter(ParameterExpression parameter) 
     { 
      var arrayType = parameter.Type.MakeArrayType(); 
      return Expression.Parameter(arrayType, parameter.Name); 
     } 
    } 

    public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(
     this Expression<Func<T, TValue>> expression, 
     int index 
    ) 
    { 
     var visitor = new Visitor(index); 
     var nexExpression = visitor.Visit(expression.Body); 
     var parameter = visitor.GetArrayParameter(expression.Parameters.Single()); 
     return Expression.Lambda<Func<T[], TValue>>(nexExpression, parameter); 
    } 
} 

,然後你可以使用這個擴展的方法是這樣的:

Expression<Func<Test, string>> ex = m => m.Code; 
Expression<Func<Test[], string>> newEx = ex.BuildArrayFromExpression(1); 
+0

真的很好,thx! –

+0

那麼,在很遠的項目之後(相當)一段時間,我嘗試在真實場景中使用你的代碼:在使用結果表達式(類似於m => m [0] .MyItem)作爲HtmlHelper的參數.DropDownListFor'它工作正常。但是在'HtmlHelper.TextBoxFor'上使用它,我得到了類型爲MyItem的(令人討厭的)變量m,它是從作用域引用的,但它沒有被定義。知道我(醜陋)答案中給出的代碼有效,你有想法嗎?我也將搜索,但我沒有在表達式訪問者上工作太多...... –

+0

好吧,通過添加一個ParameterExpression類型的公共屬性,並在GetArrayParameter方法中實現它,得到了一個解決方案...不是那麼幹淨,但有了主意, 至少。 –

0

好,有東西(醜,只是爲了測試目的)的工作,但它使事情不清楚,所以我會等待一個更好的解決方案或建立我輸入另一種方式:

public static Expression<Func<T[], TValue>> BuildArrayFromExpression<T, TValue>(this Expression<Func<T, TValue>> expression, int index) 
{ 
    var parameter = Expression.Parameter(typeof(T[]), "m"); 
    Expression body = Expression.ArrayIndex(parameter, Expression.Constant(index)); 
    var type = typeof(T); 
    var properties = expression.Body.ToString().Split('.');//ugly shortcut for test only 
    foreach (var property in properties.Skip(1)) 
    { 
     var pi = type.GetProperty(property); 
     body = Expression.Property(body, type.GetProperty(property)); 
     type = pi.PropertyType; 
    } 
    return Expression.Lambda<Func<T[], TValue>>(body, parameter); 
} 

在使用RenderMethod,叫我List<T>的每一行(分頁/有序列表從我IQueryable<T>返回)

public override ... RenderContent(T dataItem) { 
    var helper = new HtmlHelper<T[]>(GridModel.Context, new GridViewDataContainer<T[]>(GridModel.PaginatedItems.ToArray(), GridModel.Context.ViewData)); 
    var modifiedExpression = Expression.BuildArrayFromExpression(GridModel.PaginatedItems.IndexOf(dataItem));//GridModel.PaginatedItems is the List<T> returned when "executing" the IQueryable<T> 
    var textBox = helper.TextBoxFor(modifiedExpression); 
    ... 
} 
相關問題