Sunday, 19 December 2010

Entity Framework 4 “Code First”

Все почалось з того що я вирішив освіжити свої знання в ASP MVC і заодно спробувати новий енжін разор про який я недавно писав, скажу чесно про це я не жалію і рекомендую вам ознайомитись з його синтаксисом, а ще краще спробувати написати якийсь семпл. Отож ознайомлення пройшло як ви вже зрозуміли успішно і мені захотілось написати щось більше а ніж просто стандартну CRUD(Create, Read, Update, Delete) аплікацію, для цього мені була необхідна база даних в якій я б міг зберігати дані. Спочатку вибір автоматично впав на Entity Framework  з яким я якийсь час працював, але знаючи про його нову фічу “Спочатку код” вирішив що необхідно об’єднати приємне з корисним і вивчити попутньо щось нове.

Спочатку Код!

Принцип дуже простий і він в той же час досить необхідний для початківців, їм необхідна база даних для зберігання даних і на ранніх етапах без певного досвіду важко спроектувати її такою якою б вона могла проіснувати до фіналу проекту. Кожного дня зявляються ідеї і необхідно щось підправити або додати нову фічу. Для людей не дуже добре знайомих з SQL це може стати проблемою та й просто займати трохи більше часу ніж хотілось би на це витрачати, тому ми створюємо прості класи які описують обєкти з якими ми хотілиб  працювати а база буде згенерована автоматично. В даному пості ми створимо заготовку аплікації і надалі попробуємо її вдосконалити, першим кроком для нас буде створення моделі бази даних за допомогою класу.

Наприклад ось моя “база даних”:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace Portal.Models
{
    public class Photo
    {
        [Key]
        public int PhotoID { get; set; }
        public int PlaceID { get; set; }
        public string PhotoUrl { get; set; }
        public string PhotoTitle { get; set; }
        public string PhotoInfo { get; set; }
        public string PhotoDateTime { get; set; }
        public int Rating { get; set; }
    }
    public class Comment
    {
        [Key]
        public int CommentID { get; set; }
        public int PhotoID { get; set; }
        public int PlaceID { get; set; }
        public string UserName { get; set; }
        public string CommentText { get; set; }
        public int Rating { get; set; }
        public DateTime CommentDate { get; set; }
        public string UserMail { get; set; }
    }
    public class Place
    {
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
        public string Oblast { get; set; }
        public string City { get; set; }
        public string Street { get; set; }
        public string Building { get; set; }
        public string Phone { get; set; }
        public string PhoneReception { get; set; }
        public string Descrption { get; set; }
        public string Location { get; set; }
        public virtual ICollection<Comment> Comments { get; set; }
        
        public virtual ICollection<Photo> Photos { get; set; }
        public string Logo { get; set; }
    }
    public class PortalEntities : DbContext
    {
        public DbSet<Place> Places { get; set; }
        public DbSet<Photo> Photos { get; set; }
        public DbSet<Comment> Comments { get; set; }
    }
}

Незважаючи на великий обсяг тексту це лише звичайні властивості з необхідного нам типу які відображають об’єкти Place Photo i Comment. Останній клас PortalEntities батьківським класом для якого є DbContext зробить всю необхідну нам роботу. В ньому описано три властивості типу DbSet<T> які будуть згодом представляти три таблиці з відповідними іменами. Тепер перейдемо до безпосереднього прикладу в якому ми це все заставимо працювати, для цього нам буде необхідно створити новий проект :

image

на наступному вікні я обрав пустий MVC 3 проект і в якості Razor в якості вюенжіна, в студії ми повинні побачити наступне:

Снимок

Наступний крок це додавання винуватця статті до проекту, для цього я використаю NuGet, так буде скорше але якщо Microsoft ADO.NET Entity Framework Feature Community Technology Preview 4 у вас вже встановлений то ви можете просто добавити на нього референси. Для встановлення за допомогою нугету нам необхідно ввести наступну команду:

PM> Install-Package SQLCE.EntityFramework

Після чого добавляємо в папку Models вище наведену модель PortalEntities. Тепер знову ж за допомогою NuGet швдиенько згенеруємо контроллер та сторінки, для цього спочатку наш павер шелл зробимо трохи розумнішим встановивши MvcScaffold:

PM> Install-Package MvcScaffold

А після цього виконаємо наступну команду:

PM> Scaffold-Controller -m Place -c PortalEntities

Перший параметр вказує на те що ми хочемо створити контролер та сторінки для роботи з об’єктом Place, другий параметр вказує на контекст через який ми будемо працювати з Place.

02

Вище наведені стрічки свідчать про те що нам в проект було добавлено один контроллер і декілька сторінок.

Код контролера цілком стандартний тому я його повністю наводити не буду а лише те що нас цікавить зараз:

 public class PlaceController : Controller
    {
        private PortalEntities context = new PortalEntities();
        //
        // GET: /Places/
        public ViewResult Index()
        {
            return View(context.Places.ToList());
        }
        //
        // GET: /Places/Details/5
        public ViewResult Details(int id)
        {
			Place p = context.Places.Single(x => x.ID == id);
            return View(p);
        }
      ................

Як видно з екшина деталей ми можемо звертатись до контексту за допомогою звичайного лінкю виразу. Що спрощує нам життя. Також надалі коли ми додано реальну базу даних нам не прийдеться тут взагалі нічого міняти, все що нам потрібно це прописати стрічку до БД. та це буде пізніше а поки що…

В файлі Global.asax нам необхідно змінити дефолтний контролер “Home” на наш “Place” :

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Place", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

Після компіляції та запуску ми отримаємо наступне повідомлення:

03

Не те що ми хотіли отримати… Діло тут в тому що в нас немає папки App_Data а саме там має бути створений наш файл бази даних. Через контекстне меню проекту вибираємо:

04

І нарешті отримуємо хоч якийсь результат:

05

Натискаємо лінк Create New і переходимо до створення нового об’єкту:

06

Вводимо дані і відповідно отримуємо результат:

07

Також нам доступна функціональність редагування, видаляння і перегляду деталей. Реально оцінивши час на створення цього проекту можна зробити висновок що це не більше 5 хв. і то 4 з яких пішли на створення моделі БД. В недавно створену папку добавився файл бази даних:

08

В цьому файлі міститься тільки що введена та збережена нами інформація. При цьому якщо ми зараз знову запустимо наш проект то отримаємо повідомлення про помилку “File already exists. Try using a different database name. [ File name = c:\users\bats\documents\visual studio 2010\Projects\MvcApp\MvcApp\App_Data\MvcApp.Models.PortalEntities.sdf ]” Таке повідомлення ми отримали тому що наша база даних вже існує а проект по замовчуванню хоче створити нову і .. Як обіцяє команда розробників це все буде виправлено в фінальній версії, а поки що маємо те що маємо.

Для виправлення даної ситуації можна скористатись двома варіантами

  • Переходимо до файлу AppStart_SQLCEEntityFramework.cs та виправляємо стрічку
 Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");

На

Database.SetInitializer( new RecreateDatabaseIfModelChanges<PortalEntities>());

В файлі Web.config вставляємо конекшин стрінг:

  <connectionStrings>
    <add name="PortalEntities" 
         connectionString="Data source=|DataDirectory|MvcApp.Models.PortalEntities.sdf"
         providerName="System.Data.SqlServerCe.4.0"/>
  </connectionStrings>

Знову запускаємо аплікацію перший раз вона видасть те ж саме повідомлення а далі буде працювати.

Це дозволить перестворювати базу даних кожен раз коли в нас змінилась модель, старі дані при цьому будуть втрачені, а Якщо модель не змінилась то дані відповідно втрачені не будуть.

  • Також можна використати метод який продемонстрував Скот Хенселман у своєму пості. Дуже рекомендую прочитати його якщо знання англійської дозволяють.
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0",
HostingEnvironment.MapPath("~/App_Data/"), "");

Використавши його в Web.config вставляти вже нічого не треба але після зміни моделі необхідно знову замінити вище наведену стрічку на ту що була по замовчуванню:

Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");

Отож ми вдало підключили базу до свого проекту і навіть змогли обробляти дані надалі ми попробуємо більш детально ознайомитись з фічами та правилами використання даного принципу, вдосконалимо нашу аплікацію.

Джерела :

Програма:

No comments:

Post a Comment