Friday, July 16, 2010

Аннотирование модели данных, настройка отображения модели в DynamicData и ASP.NET MVC2 Templated helpers

В этой статье мы ознакомимся с возможностью аннотирования модели данных с помощью мета-модели, с целью изменения её отображения. Это та самая описательная информация объектной модели данных, о которой мы говорили ранее, которая позволяет отобразить объектную модель в html формате.

Должен заметить, что механизм, описанный ниже, используется не только в DynamicData, но также в новом средстве быстрой разработки MVC2 — шаблонных помощников (Templated helpers). Это хелперы DisplayFor(), LabelFor(), EditorFor().

DynamicData и MVC2 позволяют гибко настраивать мета-модель с помощью аннотирования полей специальными атрибутами, например, менять отображаемые названия полей, менять тип соответствующих html-контролов, задавать правила валидации. Однако аннотируются не сами свойства в модели, а свойства специального класса-болванки (MetadataType), которые имеют те же имена, что и свойства модели. Их декорируют (аннотируют) атрибутами, которые определены в пространствах имён System.ComponentModel и System.ComponentModel.DataAnnotations. Так происходит потому, что во-первых, классы модели обычно генерируются автоматически, поэтому последующее их редактирование нецелесообразно, а во-вторых, правильно отделять описание представления и правила валидации модели от неё самой.

К примеру, LINQ to SQL сгенерировал класс Product для нашей Northwind базы данных (см. предыдущий пост). Этот класс соответствует таблице Products. Найти его можно в файле Northwind.designer.cs.

[Table(Name="dbo.Products")]
public partial class Product : INotifyPropertyChanging, INotifyPropertyChanged
{
  /// ... private fields
  /// ... Extensibility Method Definitions
  public Product() {// constructor code}

  [Column(Storage="_ProductID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true)]
  public int ProductID
  {
    get
    {
      return this._ProductID;
    }
    set
    {
      if ((this._ProductID != value))
      {
        this.OnProductIDChanging(value);
        this.SendPropertyChanging();
        this._ProductID = value;
        this.SendPropertyChanged("ProductID");
        this.OnProductIDChanged();
      }
    }
  }

  [Column(Storage="_ProductName", DbType="NVarChar(40) NOT NULL", CanBeNull=false)]
  public string ProductName
  {
    // ...
  }

  [Column(Storage="_SupplierID", DbType="Int")]
  public System.Nullable<int> SupplierID
  {
    // ...
  }

  [Column(Storage="_CategoryID", DbType="Int")]
  public System.Nullable<int> CategoryID
  {
    // ...
  }
  // ... etc.
}

Обратите внимание, что этот класс partial, значит его можно расширить собственными методами и свойствами. Для того, чтобы аннотировать его мета-данными, необходимо декорировать определение этого класса атрибутом MetadataTypeAttribute. Создаём отдельную папку для мета-данных, в ней файл Product.cs и пишем следующее:

[ScaffoldTable(true)]// Можно назначить как тут, так и в ProductMetaModel
[MetadataType(typeof(ProductMetaModel))]
public partial class Product {}

[TableName("Товары")]
public class ProductMetaModel
{
  [DisplayName("ID товара")]
  public object ProductID { get; set; }

  [DisplayName("Название")]
  public object ProductName { get; set; }

  [DisplayName("Поставщик")]
  public object SupplierID { get; set; }

  [DisplayName("Категория")]
  public object CategoryID { get; set; }

  [DisplayName("Количество в упаковке")]
  public object QuantityPerUnit { get; set; }

  [DisplayName("Цена за упаковку")]
  public object UnitPrice { get; set; }

  [DisplayName("Упаковок на складе")]
  public object UnitsInStock { get; set; }

  [DisplayName("Упаковок заказано")]
  [ScaffoldColumn(false)]
  public object UnitsOnOrder { get; set; }

  [ScaffoldColumn(false)]
  public object ReorderLevel { get; set; }

  [ScaffoldColumn(false)]
  public object Discontinued { get; set; }

  [ScaffoldColumn(false)]
  public object Order_Details{ get; set; }

  [ScaffoldColumn(false)]
  public object Category{ get; set; }

  [ScaffoldColumn(false)]
  public object Supplier{ get; set; }
}

Теперь при генерации представления, будут выводиться значения только тех полей, для которых атрибут ScaffoldColumn имеет значения true (по умолчанию), подписи к полям будут соответсвовать строкам, заданым в атрибутах DisplayName, а название таблицы — TableName.

Опробуем это с помощью механизма Templated helpers. Создадим в методе Index HomeController объект Product:

var product = new DynamicDataMvcScaffolding.Models.Product()
      {
        CategoryID = 1,
        ProductName = "Картошка",
        ProductID = 123,
        SupplierID = 2,
        QuantityPerUnit = "50 кило",
        UnitPrice = 234,
        UnitsInStock = 1000,
        UnitsOnOrder = 111

      };
      ViewData["product"] = product;

В соответсвующем представлении:

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
  <div>
  <%= Html.DisplayFor(product => ViewData["product"]) %>
  </div>
</asp:Content>

Результат:

Выглядит он не очень круто, но мы с этим сможем справиться в дальнейшем :)

No comments:

Post a Comment