Уроки по FluentNHibernate c ASP.NET MVC и SQL Server. Часть 1
Часть 2. Создание классов, маппингов и заполнение БД
В предыдущей части было рассмотрено создание классов и маппинг-классов к ним. В 3 кратких таблицах были представлены маппинги связей один-к-одному, один-ко-многим, многие-ко-многим. Заполнили таблицы в sql server'e данными. В этой статье отобразим данные из БД в представлении View. Вначале добавим изображения в проект.
Отображение данных из таблицы в представление
Прежде чем создавать представление, для начала добавим в приложение изображения. Создадим в папке Content папку img.
и добавим туда изображения, к примеру, обложки этих книг.
yadi.sk/a/OZVgEhJ1iXukT
Далее изменим контроллер, чтобы он отображал данные, связанные с книгой.
---код Lazyloading загрузки — ---Query Over---
using (ISession session = NHibernateHelper.OpenSession()) {
Genre genreAl = null;
Author authorAl = null;
Series seriesAl = null;
var books = session.QueryOver<Book>()
//Left Join с таблицей Genres
.JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin)
.JoinAlias(p => p.Authors, () => authorAl, JoinType.LeftOuterJoin)
.JoinAlias(p => p.Series, () => seriesAl, JoinType.LeftOuterJoin)
//Убирает повторяющиеся id номера таблицы Book.
.TransformUsing(Transformers.DistinctRootEntity).List();
return View(books);
}
Небольшое объяснение
- var books = session.QueryOver<Book>() — подобно выполнению скрипта SQL: Select * From Book;
- .JoinAlias(p => p.Genres, () => genreAl, 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 - .TransformUsing(Transformers.DistinctRootEntity) — Подобно выполнению скрипта SQL: SELECT distinct Book.Id..., (убирает дублирующие записи с одинаковыми id)
Виды объединений
.JoinAlias(p => p.Genres, () => genreAl, JoinType.LeftOuterJoin)
- LeftOuterJoin — выбирает все записи из левой таблицы (Book), а затем присоединяет к ним записи правой таблицы (Genre). Если не найдена соответствующая запись в правой таблицы, отображает её как Null
- RightOuterJoin действует в противоположность LEFT JOIN — выбирает все записи из правой таблицы (Genre), а затем присоединяет к ним записи левой таблицы (Book)
- InnerJoin — выбирает только те записи из левой таблиц (Book) у которой есть соответствующая запись из правой таблицы (Genre), а затем присоединяет к ним записи из правой таблицы
Далее переделаем страницу Index.cshtml следующим образом.
@model IEnumerable<BibliotecaTutor.Models.Book>
@{ Layout = null; }
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<style> th,td {border: 1px solid; } </style>
</head>
<body>
<p>@Html.ActionLink("Create New", "Create")</p>
<table>
<tr>
<th>@Html.DisplayNameFor(model => model.Image)</th>
<th>@Html.DisplayNameFor(model => model.Name)</th>
<th>@Html.DisplayNameFor(model => model.MfRaiting)</th>
<th>@Html.DisplayNameFor(model => model.Mind)</th>
<th>@Html.DisplayNameFor(model => model.Series)</th>
<th>@Html.DisplayNameFor(model => model.Genres)</th>
<th>@Html.DisplayNameFor(model => model.Authors)</th>
<th>Операции</th>
</tr>
@foreach (var item in Model) {
<tr>
<td> <img src="~/Content/img/@item.Image" /></td>//Ссылка на рисунки
<td>@Html.DisplayFor(modelItem => item.Name)</td>
<td>@Html.DisplayFor(modelItem => item.MfRaiting)</td>
<td>@Html.DisplayFor(modelItem => item.Mind.MyMind)</td>
@{string strSeries = item.Series!=null?item.Series.Name:null;}
<td>@Html.DisplayFor(modelItem => strSeries)</td>
<td>
@foreach (var genre in item.Genres) {
string strGenre = genre!= null ? genre.Name : null;
@Html.DisplayFor(modelItem => strGenre) <br />
}
</td>
<td>
@foreach (var author in item.Authors) {
string strAuthor = author != null ? author.Name : null;
@Html.DisplayFor(modelItem => strAuthor) <br />
}
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
@Html.ActionLink("Details", "Details", new { id = item.Id }) |
@Html.ActionLink("Delete", "Delete", new { id = item.Id })
</td>
</tr>
}
</table>
</body>
</html>
После этих всех действий запускаем проект и видим вот такую таблицу:
Маппинг для классов, у которых есть наследование.
А как маппить классы у которых есть наследование? Допустим, имеем такой пример:
//Класс Двумерных фигур
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<Triangle> {
public TriangleMap() {
Id(x => x.Id);
Map(x => x.Style);
Map(x => x.Height);
Map(x => x.Width);
}
}
После запуска приложения, в БД Biblioteca появится следующая (пустая) таблица
Комментарии (8)
CapitanBlood
20.08.2015 12:38+1несовсем не понимаю, зачем такой огород? если вы хотите избавиться от ленивой загрузки (не на всей сущности, а только в определенных запросах), то почему не воспользоваться fetch (thenFethc, fethcAll, etc) и не городить такой огород. Существующий код сводит на нет все прелести orm — мы все свзяи формируем вручную, и появляется сотня мест, где мы можем опечататься.
а CreateSQLQuery — это уже вообще хороший сигнал к тому, что нужно задуматься, а все ли вы делаете верно. в примерах о нем упоминать вообще не стоит. Пример — это способ показать новичкам способ использования фреймворка/библиотеки, но если сразу показать костыль, то именно этим костылем и будут подпираться все дырыQSandrew
20.08.2015 13:04то почему не воспользоваться fetch (thenFethc, fethcAll, etc)
— а слона-то я не приметил…
Статья будет изменена, спасибо за замечание.
QSandrew
20.08.2015 13:32CreateSQLQuery — в примерах о нем упоминать вообще не стоит
Тут я не согласен. Приведу пример. В таблице есть функция, которую иногда меняют. А мне требуется использовать эту функцию. Мне проще использовать CreateSQLQuery, чем при каждом изменении этой функции менять свой код. Знать об этом методе стоит, но применять его только в самых крайних случаях.justmara
20.08.2015 15:57ORM вообще и NHibernate в частности — инструменты, позволяющие абстрагировать логику от внутренней структуры и особенностей реализации хранилища данных (базы в данном случае). Как только вы задействуете ручную генерацию запросов — это значит, что вам этот NHibernate не нужен и можете сразу писать напрямую Sql/Oracle/MySqlConnection. Работая с IDataReader'ами вы и то более надёжную абстракцию поимеете, чем этот зоопарк из маппингов, fetch/list/sqlquery…
gfhfk
В первую очередь выражаю благодарность за уроки, очень удобно когда такие небольшие…
Вопрос: можно ли при выборке со связаными таблицами выбирать не все поля, а только те, которые необходимо (У автором выбрать только название)?
В больших проектах выбирать все поля со всех таблиц накладно
QSandrew
Я правильно понял, вы хотите выбрать записи из таблицы Book и Author, принадлежащие определенному автору, а остальные таблицы проигнорировать?
gfhfk
Нет. Запрос пусть будет тот же. Но со связанных таблиц выбирать только некоторые поля.