Часть 3. Отображение данных из таблицы (Операция LIST)

Предыдущие части.
Уроки по FluentNHibernate c ASP.NET MVC и SQL Server. Часть 1
Часть 2. Создание классов, маппингов и заполнение БД

В предыдущей части было рассмотрено создание классов и маппинг-классов к ним. В 3 кратких таблицах были представлены маппинги связей один-к-одному, один-ко-многим, многие-ко-многим. Заполнили таблицы в sql server’e данными. В этой статье отобразим данные из БД в представлении View. Вначале добавим изображения в проект.
Отображение данных из таблицы в представление
Прежде чем создавать представление, для начала добавим в приложение изображения. Создадим в папке Content папку img.
4cf9b247cb4d4ff5a11a846fcf8dad40.png

и добавим туда изображения, к примеру, обложки этих книг.
yadi.sk/a/OZVgEhJ1iXukT

Далее изменим контроллер, чтобы он отображал все данные, связанные с книгой.

 public ActionResult Index()
{
       using (ISession session = NHibernateHelper.OpenSession()) {
             //var book = session.Query().ToList(); //Отображение данных из таблицы Book
             //Создаем критерий для выборки книг
             var criteria = session.CreateCriteria();
             //Left Join с таблицей Genres 
             criteria.CreateAlias("Genres", "genre", JoinType.LeftOuterJoin);
             criteria.CreateAlias("Series", "series", JoinType.LeftOuterJoin);
             criteria.CreateAlias("Mind", "mind", JoinType.LeftOuterJoin);
             criteria.CreateAlias("Authors", "author", JoinType.LeftOuterJoin);
             //Убирает повторяющиеся id номера таблицы Book.
             criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
            //Приводит критерий к коллекции list
             var books = criteria.List();
             return View(books);
       }
}

Небольшое объяснение

  1. var criteria = session.CreateCriteria();  — подобно выполнению скрипта SQL: Select * From Book;
  2. criteria.CreateAlias («Genres», «genre», JoinType.LeftOuterJoin);  — подобно выполнению скрипта SQL:
    SELECT *FROM Book
    inner JOIN Book_Genre ON book.id = Book_Genre.Book_id
    LEFT JOIN Genre ON Book_Genre.Genre_id = Genre.id
  3. criteria.SetResultTransformer (new DistinctRootEntityResultTransformer ());  — Подобно выполнению скрипта SQL: SELECT distinct Book.Id…, (убирает дублирующие записи с одинаковыми id)


criteria.CreateAlias («Genres», «genre», JoinType.LeftOuterJoin);  — первый аргумент «Genres» должен быть такой же, как и наименование коллекции в классе Book: public virtual ISetGenres{ get; set; }.
Второй аргумент «genre» — это псевдоним, который мы будем использовать в критериях.

Виды объединений
criteria.CreateAlias («Genres», «genre», JoinType.LeftOuterJoin);

  1. LeftOuterJoin — выбирает все записи из левой таблицы (Book), а затем присоединяет к ним записи правой таблицы (Genre). Если не найдена соответствующая запись в правой таблицы, отображает её как Null
  2. RightOuterJoin действует в противоположность LEFT JOIN — выбирает все записи из правой таблицы (Genre), а затем присоединяет к ним записи левой таблицы (Book)
  3. InnerJoin — выбирает только те записи из левой таблиц (Book) у которой есть соответствующая запись из правой таблицы (Genre), а затем присоединяет к ним записи из правой таблицы

Далее переделаем страницу Index.cshtml следующим образом.

Код представления
 @model IEnumerable
@{    Layout = null; }



    
      Index
       


    

@Html.ActionLink("Create New", "Create")

@foreach (var item in Model) { //Ссылка на рисунки }
@Html.DisplayNameFor(model => model.Image) @Html.DisplayNameFor(model => model.Name) @Html.DisplayNameFor(model => model.MfRaiting) @Html.DisplayNameFor(model => model.Mind) @Html.DisplayNameFor(model => model.Series) @Html.DisplayNameFor(model => model.Genres) @Html.DisplayNameFor(model => model.Authors) Операции
@Html.DisplayFor(modelItem => item.Name) @Html.DisplayFor(modelItem => item.MfRaiting) @Html.DisplayFor(modelItem => item.Mind.MyMind) @Html.DisplayFor(modelItem => item.Series.Name) @foreach (var genre in item.Genres) { @Html.DisplayFor(modelItem => genre.Name)
}
@foreach (var author in item.Authors) { @Html.DisplayFor(modelItem => author.Name)
}
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) | @Html.ActionLink("Details", "Details", new { id = item.Id }) | @Html.ActionLink("Delete", "Delete", new { id = item.Id })

После этих всех действий запускаем проект и видим вот такую таблицу:
e26eae77e15949aa8d3a85e856961e42.jpg

Дополнительный материал.
Знакомые с Nhibernate, увидев следующие строчки

             //Создаем критерий для выборки книг
             var criteria = session.CreateCriteria();
             //Left Join с таблицей Genres
             criteria.CreateAlias("Genres", "genre", JoinType.LeftOuterJoin);
             ******
             DistinctRootEntityResultTransformer());
             var books = criteria.List();


могут спросить: «Ну и где тут FluentNhibernate, поддержка intellisense?» выкладываю запись, предназначенная только для fluentNhibernate, действие которой аналочно действию коду выше.

             //Методы с поддержкой intellisense
             Book bookAl = null; Genre genreAl = null;
             IQueryOver bookQuery =
             session.QueryOver(() => bookAl)
             .JoinAlias(() => bookAl.Genres, () => genreAl, JoinType.LeftOuterJoin)
             .TransformUsing(Transformers.DistinctRootEntity);
              var books = bookQuery.List();

Мне он не нравится тем, что нужно создавать лишние (в данном примере) переменные, которые я далее в коде не использую, да и следующие значения, «IQueryOver» и »()» — не совсем понятны, да и кода на две строчки больше… Какой из методов использовать, конечно же, решать программисту.

Для тех, кто любит писать SQL-скрипты, у Nhibernate найдется и для таких инструмент:

//SQl-запрос
var queryBook = string.Format(@"select Id, Name, Description, MfRaiting, PageNumber, Image, IncomeDate from Book");
//Вывод списка всех книг
var books = session.CreateSQLQuery(queryBook)
//SQl-запрос ассоциирует с классом
.SetResultTransformer(Transformers.AliasToBean(typeof(Book)))
.List().ToList();

*Небольшое замечание… в отличие от SQL-скрипта, тут учитываются регистр букв, то есть Id и id — это разные слова.
**Также мы должны выбрать ВСЕ поля, которые есть в классе Book, нельзя выбрать, к примеру только Id и Name. Если ж требуется выбрать определенные поля, то под них следует создать новый класс.
***Почему мы перечислили все поля, а не использовали следующий скрипт Select * from Book? В таблице Book у нас есть поле Series_Id, которого нет в классе Book

Маппинг для классов, у которых есть наследование.
А как маппить классы у которых есть наследование? Допустим, имеем такой пример:

 //Класс Двумерных фигур
public class TwoDShape {
    //Ширина
    public virtual int Width { get; set; }
    //Высота
    public virtual int Height { get; set; }
}
//Класс треугольник
public class Triangle : TwoDShape {
    //Идентификационный номер
    public virtual int Id { get; set; }
    //Вид треугольника
    public virtual string Style { get; set; }
}

В принципе, ничего сложного в этом маппинге нет, мы просто создадим один маппинг для производного класса, то есть таблицы Triangle.

 //Маппинг треугольника
public class TriangleMap : ClassMap {
       public TriangleMap() {
             Id(x => x.Id);
             Map(x => x.Style);
             Map(x => x.Height);
             Map(x => x.Width);
       }
}


После запуска приложения, в БД Biblioteca появится следующая (пустая) таблица
f3954021af9d4fc7b172d6fcc939564f.png

© Habrahabr.ru