2012-01-11 4 views
4

Контекст: Поскольку мы разрабатываем C# MVC3, мы хотели иметь некоторые классы, предназначенные для обработки таблиц на веб-странице. (Разбиение на страницы/поиск/и т. Д.).Lambda Expression присваивает исходный объект

Таким образом, мы, наконец, нашли, что это могло бы быть лучше, чтобы иметь следующие классы:

объекта таблицы, который будет содержать все другие объекты и знает текущей страница/текущий поиск и т.д. ... (информация разной)

public class Table<T> where T : IPrivateObject 
{ 
    ... 

    public ICollection<Column<T>> Columns { get; set; } 
    public ICollection<Row<T>> Rows { get; set; } 
    public ICollection<RowMenu<T>> Menus { get; set; } 

    public ICollection<T> Items { get; set; } 

    public Table(
     ICollection<T> inputItems, 
     ICollection<Column<T>> columns, 
     ICollection<RowMenuItem<T>> rowMenuItems, 
     ...) 
     { 
      ... 
      this.Columns = columns; 
     } 

объект столбец, который знает, какое свойство должно отображаться и а значение

public class Column<T> where T : IPrivateObject 
{ 
    public string Value { get; set; } 
    public Expression<Func<T, object>> Property { get; set; } 

    public Column(Expression<Func<T, object>> property, string value) 
    { 
     this.Property = property; 
     this.Value = value; 
    } 
} 

заголовка другие классы не очень интересно поэтому я не буду публиковать их здесь.

В контроллере, мы используем эти классы так:

public ActionResult Index(string search = null, string sort = null, int order = 1, int take = 10, int page = 1) 
    { 
     ICollection<Person> people = prismaManager.PersonManager.Search(search); 
     ICollection<Column<Person>> columns= new List<Column<Person>>(); 
     columns.Add(new Column<Person>(Person => Person, "Person")); 
     columns.Add(new Column<Person>(Person => Person.LastMembershipApproval, "Last Membership approval")); 
     Table<Person> table = people.ToTable(columns); 
    } 

Мы сейчас пишем помощник, который будет правильно отображать таблицу. Это хорошо работает для заголовка, но мы сталкиваемся с проблемой с выражениями, когда хотим использовать помощник @ Html.DisplayFor().

Это то, что мы в настоящее время для содержания:

private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject 
    { 
     StringBuilder sb = new StringBuilder(); 
     foreach (var item in table.Items) 
     { 
      sb.AppendLine("<tr>"); 
      foreach (var column in table.Columns) 
      { 
       sb.AppendLine("<td>"); 
       sb.AppendLine(helper.DisplayFor(obj => ???).ToString()); // How should I use the Expression that is stored in the column but for the current element ? 
       sb.AppendLine("</td>"); 
      } 
      sb.AppendLine("</tr>"); 
     } 
     return sb.ToString(); 
    } 

Для этого, чтобы работать, мы должны установить значение параметра «Человек» из выражения, сохраненного в столбце текущего элемента.

new Column<Person>(Person => Person, "Person")); 

Как мы должны это делать? Должны ли мы (если это возможно) изменить выражение для установки значения? Должно ли мы воссоздать новое выражение с использованием старого в качестве основного выражения?

Я искал 3 дня и не могу найти ответы.

Благодарим за помощь.

ОБНОВЛЕНИЕ:

Проблема состоит в том (как сказал @Groo & @Darin Димитров), что помощник имеет тип HtmlHelper> и не HtmlHelper. Любая идея, как я могу получить HtmlHelper из HtmlHelper>?

UPDATE:

Person класс следующим образом:

public class Person : IPrivateObject 
{ 
    public int Id { get; set; } 
    public int? AddrId { get; set; } 

    [DisplayName("First Name")] 
    [StringLength(100)] 
    [Required] 
    public string FirstName { get; set; } 

    [DisplayName("Last Name")] 
    [StringLength(100)] 
    [Required] 
    public string LastName { get; set; } 

    [DisplayName("Initials")] 
    [StringLength(6)] 
    public string Initials { get; set; } 

    [DisplayName("Last membership approval")] 
    public Nullable<DateTime> LastMembershipApproval { get; set; } 

    [DisplayName("Full name")] 
    public string FullName 
    { 
     get 
     { 
      return FirstName + " " + LastName; 
     } 
    } 
    public override string ToString() 
    { 
     return FullName; 
    } 
} 

ответ

4

Вот как можно дальше. Начните писать реализацию контейнера данных пользовательского вида, которые могли бы быть столь же просто, как:

public class ViewDataContainer : IViewDataContainer 
{ 
    public ViewDataContainer(ViewDataDictionary viewData) 
    { 
     ViewData = viewData; 
    } 

    public ViewDataDictionary ViewData { get; set; } 
} 

, а затем просто создать экземпляр HtmlHelper<T> который, что вам нужно:

private static string TableRows<T>(HtmlHelper<Table<T>> helper, Table<T> table) where T : IPrivateObject 
{ 
    var sb = new StringBuilder(); 
    sb.AppendLine("<table>"); 
    foreach (var item in table.Items) 
    { 
     sb.AppendLine("<tr>"); 
     foreach (var column in table.Columns) 
     { 
      var viewData = new ViewDataDictionary<T>(item); 
      var viewContext = new ViewContext(
       helper.ViewContext.Controller.ControllerContext, 
       helper.ViewContext.View, 
       new ViewDataDictionary<T>(item), 
       helper.ViewContext.Controller.TempData, 
       helper.ViewContext.Writer 
      ); 
      var viewDataContainer = new ViewDataContainer(viewData); 
      var itemHelper = new HtmlHelper<T>(viewContext, viewDataContainer); 

      sb.AppendLine("<td>"); 
      sb.AppendLine(itemHelper.DisplayFor(column.Property)); 
      sb.AppendLine("</td>"); 
     } 
     sb.AppendLine("</tr>"); 
    } 
    sb.AppendLine("</table>"); 
    return sb.ToString();  
} 

UPDATE:

Предыдущий пример не обрабатывает типы значений, потому что выражение в столбце имеет тип Expression<Func<T, object>> и когда вы указываете на свойство типа значения th e будет помещен в коробку, и ASP.NET MVC не позволяет использовать такие выражения с помощью помощников шаблона. Чтобы устранить эту проблему, одна из возможностей заключается в том, чтобы проверить, было ли значение в коробке и извлечь фактический тип:

sb.AppendLine("<td>"); 
var unary = column.Property.Body as UnaryExpression; 
if (unary != null && unary.NodeType == ExpressionType.Convert) 
{ 
    var lambda = Expression.Lambda(unary.Operand, column.Property.Parameters[0]); 
    sb.AppendLine(itemHelper.Display(ExpressionHelper.GetExpressionText(lambda)).ToHtmlString()); 
} 
else 
{ 
    sb.AppendLine(itemHelper.DisplayFor(column.Property).ToHtmlString()); 
} 
sb.AppendLine("</td>"); 
+0

Спасибо за вашу помощь. Он отлично работает для выражения Person => Person. Но это не для свойств в Person ... Я получаю исключение: «Шаблоны могут использоваться только с доступом к полям, доступом к ресурсам, индексу одномерного массива или однопараметрическим пользовательским выражениям индексатора». когда приходит очередь на выражение «Person => Person.LastMembershipApproval» для оценки ... Любая идея, почему? – Whoami

+0

@Whoami, странно, это сработало, когда я его протестировал. Если вы пришлете мне образец проекта, иллюстрирующий проблему, я могу помочь вам. –

+0

Я добавлю класс Person, чтобы вы могли проверить. ** ОБНОВЛЕНИЕ ** Добавлен класс. – Whoami

1

Вы должны скомпилировать выражение, используя expression.Compile() (у вас есть выражение свойства в вашем column.Property). Это даст вам делегата. Вы можете передать объект туда и получить значение. Вам также нужно будет передать человека, или Т, вспомогательному методу.

+0

Спасибо за ваш ответ, но это не сработает. Он не может использоваться с шаблоном DisplayFor, поскольку я получаю следующее исключение: Шаблоны могут использоваться только с доступом к полю, доступом к ресурсам, индексом одномерного массива или однопараметрическими пользовательскими выражениями индексатора. – Whoami

+0

Любая идея, как я мог бы нарисовать/создать новый HtmlHelper типа T, чтобы я мог использовать его для DisplayFor? – Whoami

+0

о! поэтому вы хотите использовать его в привязке? вам нужно имя свойства из выражения лямбда? – ivowiblo

2

Есть несколько вещей, которые вы должны изменить.

  1. Дело в том, что первый меня удивило то, что ваш стол и имеет список столбцов и Ряды. Вы должны изменить дизайн на что-то вроде: в таблице есть список строк, и у каждой строки есть список столбцов (или наоборот).

    Но это замечание менее актуально. Я думаю, что «Столбец» - это что-то вроде «Определение столбца» и не содержит данных, но в этом случае я не вижу смысла иметь ICollection<Row<T>> вместо ICollection<T>.

  2. Далее вы, вероятно, захотите сохранить делегат, например Func<T, object>, а не Expression<Func<T, object>>.

  3. Property должен иметь хотя бы частные сеттеры (или, что еще лучше, поля для чтения только для чтения). Это не то, что вы хотели бы изменить другие части вашего кода.

  4. Именование очень смущает ИМХО. Я бы выбрал лучшие имена свойств. Если я правильно вас понял, Value и Property действительно должны называться HeaderName и GetValue, соответственно.

Сказав все это, я хотел бы изменить Column к чему-то вроде этого:

public class Column<T> where T : IPrivateObject 
{ 
    private readonly string _name; 
    private readonly Func<T, object> _valueGetter; 

    /// <summary> 
    /// Gets the column name. 
    /// </summary> 
    public string Name 
    { 
     get { return _name; } 
    } 

    /// <summary> 
    /// Gets the value of this column from the 
    /// specified object. 
    /// </summary> 
    /// <param name="obj">The object.</param> 
    /// <returns></returns> 
    public object GetValueFrom(T obj) 
    { 
     return _valueGetter(obj); 
    } 

    public Column(string columnName, Func<T, object> valueGetter) 
    { 
     _name = columnName; 
     _valueGetter = valueGetter; 
    } 
} 

А затем просто использовать это в цикле:

sb.AppendLine(column.GetValueFrom(item).ToString()); 
+0

Спасибо за ваш быстрый ответ! Но для 1. Я не согласен, поскольку таблица управляется столбцами. Строка только здесь, если есть что-то особенное для строки (например,Строка с personId% 2 == 0 должна отображаться красным цветом или что угодно). В остальном я согласен и ценю ваш ответ. Спасибо – Whoami

+0

@Whoami: Я обновил ответ. Я считаю, что последняя строка - это то, о чем вы просили. – Groo

+0

Да, это то, о чем я просил. Я попробую это и проверю, можно ли его использовать с помощником DisplayFor. – Whoami