четверг, 28 июля 2011 г.

MVC - часть3. CRUD логика (Detail)


Итак, в прошлой статье мы получили список объектов карты. Возле каждого объекта у нас имеется ссылки, в том числе и «Details». Вот о ней и поговорим. Сама по себе страница с подробностями об объекте не так интересна, но у нас же карта, так давайте вместе с подробностями отобразим небольшую карту, где бы отображался этот самый объект.
Сперва пишем экшен контроллера (листинг 1), а затем генерируем представление (листинг 2).
Листинг 1Экшен контроллера
        [HttpGet]
        public ActionResult MapObject_Details(long id)
        {
            if (id != 0)
            {
                DataManager db = new DataManager();

                return View(db.GetMapObject(id));
            }
            else
            {
                return View();
            }
        }
Листинг 2Представление Details
<fieldset>
    <legend>Map</legend>

    <div class="display-label">ObjName</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ObjName)
    </div>

    <div class="display-label">ObjAddress</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ObjAddress)
    </div>

    <div class="display-label">ObjType</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ObjType_id)
    </div>

    <div class="display-label">Description</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Description)
    </div>

    <div class="display-label">Contact</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Contact)
    </div>

    <div class="display-label">Longitude</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Longitude)
    </div>

    <div class="display-label">Latitude</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Latitude)
    </div>

    <div class="display-label">ObjType_id</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.ObjType_id)
    </div>

    <div class="display-label">Author</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Author)
    </div>

    <div class="display-label">CreateDate</div>
    <div class="display-field">
        @Html.DisplayFor(model => model.CreateDate)
    </div>
</fieldset>

Дополним эту разметку еще одним полем – скрытым:
@Html.HiddenFor(model => model.MapObj_id)

В этом скрытом поле на нашей странице будет присутствовать идентификатор объекта. Зачем он нам? А мы по его значению будем на карту грузить его балун. Рассмотрим, как. Для начала сделаем метод нашего менеджера данных, который по идентификатору будет отдавать данные об объекте (листинг 3).
Листинг 3 – Получаем данные объекта по идентификатору
        public List<object> GetMapObjectById(long mapObjId)
        {
            List<object> list = new List<object>();

            Map item = GetMapObject(mapObjId);

            var data = new
            {
                Name = item.ObjName,
                Style = item.MapObjType.Style,
                Latitude = item.Latitude,  //широта
                Longitude = item.Longitude, //долгота
                Html = "<table class='info'>" +
                        "<tr><th>Название</th><td>" + item.ObjName + "</td></tr>" +
                        "<tr><th>Адрес</th><td>" + item.ObjAddress + "</td></tr>" +
                        "<tr><th>Описание</th><td>" + item.Description + "</td></tr>" +
                        "<tr><th>Контакты</th><td>" + item.Contact + "</td></tr>" +
                        "<tr><th>Оценка</th><td>" + ((item.MiddleRating != 0) ? item.MiddleRating.ToString() : "нет") + "</td></tr>" +
                        "<tr><th><a href='/Home/Comment/" + item.MapObj_id + "'>Отзывы</a></th><td></td></tr>" +
                        "</table>",      //код для балуна
            };

            list.Add(data);

            return list;
        }

Как видно, тут мы формируем код балуна, который позже загрузим в js-скрипте.
Теперь пишем экшен, который запакует это все дело в JSON (листинг 4).
Листинг 4 – Экшен, передающий данные в представление
        [HttpGet]
        public JsonResult ObjectDetails(long mapObjId)
        {
            DataManager db = new DataManager();

            return Json(db.GetMapObjectById(mapObjId), JsonRequestBehavior.AllowGet);
        }

Остался скрипт, который загрузит наш JSON на карту (листинг 5).
Листинг 5 – Скрипт отображения данных на карте
<script src="http://api-maps.yandex.ru/1.1/index.xml?key=AAFDuU0BAAAAWmDlWgMAn8sg2dtfRwaCuNPYn0etp5qnk6QAAAAAAAAAAAAwqKOA3GzAs8EObkeouS3eI9xZMA=="
        type="text/javascript">
    </script>

    <script type="text/javascript" src="../../Scripts/jquery-1.5.1.min.js">
    </script>

    <script type="text/javascript" >
        var group = [];
        var map;

        $(document).ready(function () {
            // Найдем на странице html элемент в котором будет размещаться карта
            map = new YMaps.Map(YMaps.jQuery("#YMapsID")[0]);

            map.setCenter(new YMaps.GeoPoint(55.975669, 54.732613), 10);

            // добавим контролы
            map.addControl(new YMaps.ToolBar());
            map.addControl(new YMaps.Zoom());
            map.addControl(new YMaps.TypeControl());
            map.enableScrollZoom();

            var searchControl = new YMaps.SearchControl({
                resultsPerPage: 2,  // Количество объектов на странице
                useMapBounds: 1     // Объекты, найденные в видимой области карты
                // будут показаны в начале списка
            });
            map.addControl(searchControl);

            getObject(document.getElementById('MapObj_id').value);
        });

        // объект по идентификатору
        function getObject(mapObjId) {

            $.getJSON("/Home/ObjectDetails", { mapObjId: mapObjId }, function (mapObj) {

                var s;

                group["new"] = new YMaps.GeoObjectCollection("default#");

                $.each(mapObj, function (i) {

                    // Создание стиля для значка метки
                    s = new YMaps.Style();
                    s.iconStyle = new YMaps.IconStyle();
                    s.iconStyle.href = this.Style;
                    s.iconStyle.size = new YMaps.Point(18, 18);
                    s.iconStyle.offset = new YMaps.Point(-9, -9);
                    s.balloonContentStyle = new YMaps.BalloonContentStyle(
                            new YMaps.Template("<div>$[description]</div>")
                    );

                    var point = new YMaps.GeoPoint(this.Longitude, this.Latitude);
                    var placemark = new YMaps.Placemark(point, { hasHint: 1, style: s });
                    placemark.name = this.Name;

                    //тут засовываем html
                    placemark.description = this.Html + "<br />";

                    group["new"].add(placemark);
                });

                map.addOverlay(group["new"]);
            });
        };

    </script>

В общем то, схема уже нам знакома, комментировать нечего.
Результат на рисунке 1.

Рисунок 1 – Подробности об объекте карты


четверг, 21 июля 2011 г.

MVC - часть3. CRUD логика (Edit)


Давайте теперь попробуем сгенерировать страницу для редактирования объекта нашей карты.
Если изучить ссылки на редактирование в списке объектов, то можно обнаружить, что они все уникальны и содержат в себе идентификатор объекта карты. Вот этот идентификатор и будет «ловить» экшен, отвечающий за редактирование. Давайте приступим. Для начала нам нужен метод нашего MapDataManager, который бы отдавал нам объект карты по его идентификатору (листинг 1).
Листинг 1 – Получение объекта по его идентификатору
        public Map GetMapObjet(long mapObjId)
        {
            return _db.Map.SingleOrDefault(it => it.MapObj_id == mapObjId);
        }

Тут, думаю, все понятно. Едем дальше – пишем экшен для получения формы для редактирования объекта (листинг 2).
Листинг 2 – Получение формы для редактирования объекта
        [HttpGet]
        public ActionResult MapObject_Edit(long id)
        {
            DataManager dm = new DataManager();
           
            return View(dm.GetMapObjet(id));
        }

Тут мы передаем в представление объект карты, полученный по идентификатору. Напомню, что этот идентификатор «пришел» в экшен из ссылки на странице со списком объектов. Теперь нам надо зафиксировать наши изменения в БД при отправке формы. Для этого сначала «научим» MapDataManager сохранять объект в БД (листинг 3).
Листинг 3 – Сохранение объекта в БД
        public void SaveMapObject(Map mapObj)
        {
            Map old = GetMapObjet(mapObj.MapObj_id);

            old.ObjName = mapObj.ObjName;
            old.ObjAddress = mapObj.ObjAddress;
            old.ObjType = mapObj.ObjType;
            old.Description = mapObj.Description;
            old.Contact = mapObj.Contact;
            old.Longitude = mapObj.Longitude;
            old.Latitude = mapObj.Latitude;

            _db.SaveChanges();
        }

 Тут мы передаем в процедуру новый объект. Для фиксации его отредактированных свойств мы сначала ищем старый (нередактированный) объект – и по очереди обновляем все его свойства. Далее – сохраняем. Теперь напишем экшен пост-формы для этого дела (листинг 4).
Листинг 4 – Отправка формы с измененным объектом
        [HttpPost]
        public ActionResult MapObject_Edit(Map mapObj)
        {
            if (ModelState.IsValid)
            {
                DataManager dm = new DataManager();

                dm.SaveMapObject(mapObj);

                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }

Как говорится, без комментариев.
Если вы обратили внимание, мы наш экшен назвали  MapObject_Edit, в то время как в представлении со списком объектов студия обозвала этот экшен просто Edit, поэтому переименуйте эту ссылку вручную – и пользуйтесь.
Таким нехитрым способом буквально за пару минут мы создали страницу для редактирования объекта карты.



пятница, 8 июля 2011 г.

MVC - часть3. CRUD логика (List)

В прошлой статье мы остановились на том, что создали Entity Framework модель по нашей БД. Давайте немного усложним нашу БД, чтоб было интереснее и немного сложнее. Допустим, будем на карте отображать объекты, полезные автомобилисту – автосервисы и автозаправочные станции (АЗС). Создадим таблицы для хранения наших данных (листинг 1).
Листинг 1 – Таблицы с данными об объектах карты
CREATE TABLE [dbo].[Map] (
  [MapObj_id] bigint IDENTITY(1, 1) NOT NULL,
  [ObjName] nvarchar(512) COLLATE Cyrillic_General_CI_AS NULL,
  [ObjAddress] nvarchar(512) COLLATE Cyrillic_General_CI_AS NULL,
  [ObjType] int NULL,
  [Description] nvarchar(max) COLLATE Cyrillic_General_CI_AS NULL,
  [Contact] nvarchar(max) COLLATE Cyrillic_General_CI_AS NULL,
  [Longitude] nvarchar(32) COLLATE Cyrillic_General_CI_AS NULL,
  [Latitude] nvarchar(32) COLLATE Cyrillic_General_CI_AS NULL,
  CONSTRAINT [CLIDX_Map_id] PRIMARY KEY CLUSTERED ([MapObj_id])
)

CREATE TABLE [dbo].[Comments] (
  [Comm_id] bigint IDENTITY(1, 1) NOT NULL,
  [MapObj_id] bigint NOT NULL,
  [CommentText] nvarchar(4000) COLLATE Cyrillic_General_CI_AS
CONSTRAINT [NNULL_Comments_Comment] DEFAULT '' NOT NULL,
  [Rating] int NOT NULL,
  [Author] nvarchar(32) COLLATE Cyrillic_General_CI_AS
CONSTRAINT [NNULL_Comment_Author] DEFAULT 'неизвестный' NOT NULL,
  CONSTRAINT [CLIDX_Comment_id] PRIMARY KEY CLUSTERED ([Comm_id]),
  CONSTRAINT [CK_Comment_Rating] CHECK ([Rating]<(11) AND [Rating]>(0)),
  CONSTRAINT [FK_Comments_MapObjId] FOREIGN KEY ([MapObj_id])
  REFERENCES [dbo].[Map] ([MapObj_id])
  ON UPDATE CASCADE
  ON DELETE CASCADE
)
Разберемся. Таблица Map – это данные об объектах карты: название, адрес, контакты, координаты, тип объекта (1 – автосервис, 2 - азс). Таблица Comments – это комментарии, которые оставляют пользователи о каких-либо объектах, содержит сам текст комментария, автора, оценку пользователя. Таблица Map связана с Comments внешним ключом отношением 1 ко многим (рисунок 1), т.е. у одного объекта может быть куча комментариев. Ну, это и понятно.
Рисунок 1 – Сущности из нашей БД

Кроме того, есть ограничение на оценку (может быть только от 0 до 10) и другие мелочи – разберетесь.
Идем в раздел Models нашего проекта и удаляем нашу модель MapDataModel. Боятся нечего, мы же помним, что архитектура MVC сильна разделением ответственности и все такое – вот мы сейчас на место удаленной модели создадим новую, остальной проект даже не заметит. Но перед этим еще один момент – в прошлой статье мы сгенерировали класс для работы с нашими данными, который назвался ProjectEntities. Чтоб все легло гладко, идем в файл Web.config, ищем там секцию connectionStrings, а в ней - наш ProjectEntities. Удаляем эту строку и со спокойной душой повторяем все шаги из прошлой статьи для нашей новой БД.
Осталось поправить в методе GetListObjectMap() нашего DataManager'а названия полей нашей обновленной таблицы.
Теперь все готово для дальнейших манипуляций.
Итак, что такое CRUD? Само слово - аббревиатура от слов Create, Read, Update, Delete. Т.е. это базовые операции управления данными. Применительно к нашему проекту программирование CRUD – логики – это создание (генерация) представлений, которые бы обеспечивали бы эти самые операции. Немного непонятно, но мы разберемся.
Давайте для начала сделаем самое простое – отобразим наши объекты карты в виде некой таблицы - списка, т.е. мы выполним операцию Read.
Для начала, научим наш DataManager формировать список объектов карты. Для этого создадим метод как в листинге 2.
Листинг 2 – Получение списка объектов карты
        public IQueryable<Map> GetMapObjets()
        {
            return _db.Map;
        }
В общем, все понятно – получаем список объектов.
Идем теперь в наш контроллер и добавляем экшен как в листинге 3.
Листинг 3 – Экшен для отображения списка объектов карты
        [HttpGet]
        public ActionResult MapObject_List()
        {
            DataManager db = new DataManager();
           
            return View(db.GetMapObjets());

        }
Тут мы создаем список объектов и отдаем их странице, реализуем экшен только с атрибутом [HttpGet], т.е. только для запроса содержимого страницы (для отправки пользовательских данных используется атрибут [HttpPost]).  После этого щелкаем правой кнопкой на нашем новом экшене и выбираем в появившемся меню Add View, появится диалог как на рисунке 2.
Рисунок 2 – Диалог создания представления

В этом диалоге выбираем опцию Create a strongly-typed view (создать типизированное представление), это позволит нашему представлению работать с какой-либо из наших моделей. Далее в списке классов моделей выберем Map (MvcApplication3.Models), т.е. наше представление будет работать с моделью объекта карты. И, самое для нас интереное, выбираем в списке Scaffold template шаблон List, т.е. просто список объектов. Не забудем указать мастер пейдж. Результат на рисунке 3.
Рисунок 3 – Настройки представления списка объектов
Представление создали. Содержимое пока изучать не будем. Нам надо создать ссылку на это представление, поэтому идем в файл _Layout.cshtml и создаем там ссылку. Запускаем. Видим прекрасное (рисунок 4).
Рисунок 4 – Список наших объектов

Отлично!
Если попробовать пройтись по ссылкам Edit, Details, Delete, то выскочит ошибка. Почему? Правильно - потому, что у нас не реализованы соответствующие экшены и представления. Попробуем сделать это в следующих статьях.
Удачи!