Создание уровня доступа к данным

по Эрик Реитан

Скачайте образец проекта Wingtip Toys (C#) или Загрузите электронную книгу (PDF)

В этой серии руководств вы узнаете об основах создания приложения ASP.NET Web Forms с помощью ASP.NET 4,5 и Microsoft Visual Studio Express 2013 для Web. Для этой серии руководств доступен проект Visual Studio 2013 с C# исходным кодом .

В этом учебнике описывается создание, доступ и проверка данных из базы данных с помощью веб-форм ASP.NET и Entity Framework Code First. Это руководство построено на предыдущем учебном курсе «создание проекта» и входит в серию руководств по магазину Wingtip Toy Store. По завершении работы с этим руководством вы будете создавать группу классов доступа к данным, которые находятся в папке Models проекта.

Из этого руководства вы узнаете, как выполнять такие задачи:

  • Создание моделей данных.
  • Инициализация и заполнение базы данных.
  • Обновление и настройка приложения для поддержки базы данных.

Ниже приведены функции, представленные в этом учебнике.

  • Entity Framework Code First
  • LocalDB
  • Заметки к данным

Создание моделей данных

Entity Framework — это платформа объектно-реляционного СОПОСТАВЛЕНИЯ (ORM). Он позволяет работать с реляционными данными как с объектами, устраняя большую часть кода доступа к данным, который обычно требуется написать. С помощью Entity Framework можно выдавать запросы с помощью LINQ, а затем получать и манипулировать данными в виде строго типизированных объектов. LINQ предоставляет шаблоны для запросов и обновления данных. Использование Entity Framework позволяет сосредоточиться на создании оставшейся части приложения, а не на основных понятиях доступа к данным. Далее в этой серии руководств мы покажем, как использовать данные для заполнения переходов и запросов продуктов.

Entity Framework поддерживает парадигму разработки, именуемую Code First. Code First позволяет определять модели данных с помощью классов. Класс — это конструкция, позволяющая создавать собственные пользовательские типы, объединяя переменные других типов, методов и событий. Можно сопоставлять классы с существующей базой данных или использовать их для создания базы данных. В этом руководстве вы создадите модели данных, создав классы модели данных. Затем вы разрешите Entity Framework создать базу данных на лету на основе этих новых классов.

Начнем с создания классов сущностей, определяющих модели данных для приложения веб-форм. Затем будет создан класс контекста, управляющий классами сущностей и предоставляющий доступ к базе данных. Также будет создан класс инициализатора, который будет использоваться для заполнения базы данных.

Entity Framework и ссылки

По умолчанию Entity Framework включается при создании нового веб-приложения ASP.NET с помощью шаблона веб-форм . Entity Framework можно установить, удалить и обновить как пакет NuGet.

Этот пакет NuGet включает в проект следующие сборки среды выполнения :

  • EntityFramework. dll — весь распространенный код среды выполнения, используемый Entity Framework
  • EntityFramework. SqlServer. dll — поставщик Microsoft SQL Server для Entity Framework

Классы сущностей

Классы, создаваемые для определения схемы данных, называются классами сущностей. Если вы не знакомы с проектированием базы данных, подумайте о классах сущностей в качестве определений таблиц базы данных. Каждое свойство в классе указывает столбец в таблице базы данных. Эти классы предоставляют упрощенный объектно-реляционный интерфейс между объектно-ориентированным кодом и реляционной табличной структурой базы данных.

В этом руководстве мы начнем с добавления простых классов сущностей, представляющих схемы для продуктов и категорий. Класс Products будет содержать определения для каждого продукта. Имя каждого из членов класса Product будет ProductID, ProductName, Description, ImagePath, UnitPrice, CategoryIDи Category. Класс category будет содержать определения для каждой категории, к которой может принадлежать продукт, например автомобиля, в форме самолета или плоскости. Имя каждого из членов класса category будет CategoryID, CategoryName, Descriptionи Products. Каждый продукт будет принадлежать к одной из категорий. Эти классы сущностей будут добавлены в папку существующих моделей проекта.

  1. В Обозреватель решенийщелкните правой кнопкой мыши папку Models и выберите добавить -> новый элемент.

    Создание уровня доступа к данным — меню "создать элемент"

    Откроется диалоговое окно Добавление нового элемента .

  2. В разделе C# визуальный элемент на панели установленные слева выберите код.

    Создание уровня доступа к данным — меню "создать элемент"

  3. Выберите класс из средней области и назовите этот новый класс Product.CS.

  4. Нажмите кнопку Добавить.
    В редакторе отобразится новый файл класса.

  5. Замените код по умолчанию на приведенный ниже:

    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Product
        {
            [ScaffoldColumn(false)]
            public int ProductID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string ProductName { get; set; }
    
            [Required, StringLength(10000), Display(Name = "Product Description"), DataType(DataType.MultilineText)]
            public string Description { get; set; }
    
            public string ImagePath { get; set; }
    
            [Display(Name = "Price")]
            public double? UnitPrice { get; set; }
    
            public int? CategoryID { get; set; }
    
            public virtual Category Category { get; set; }
        }
    }
    
  6. Создайте другой класс, повторив шаги 1 – 4, но назовите новый класс Category.CS и замените код по умолчанию следующим кодом:

    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    
    namespace WingtipToys.Models
    {
        public class Category
        {
            [ScaffoldColumn(false)]
            public int CategoryID { get; set; }
    
            [Required, StringLength(100), Display(Name = "Name")]
            public string CategoryName { get; set; }
    
            [Display(Name = "Product Description")]
            public string Description { get; set; }
    
            public virtual ICollection<Product> Products { get; set; }
        }
    }
    

Как упоминалось выше, класс Category представляет тип продукта, предназначенного для продажи приложением (например, "автомобили", "Боатс", "Роккетс"и т. д.), а класс Product представляет отдельные продукты (Toys) в базе данных. Каждый экземпляр объекта Product будет соответствовать строке в таблице реляционной базы данных, а каждое свойство класса Product будет сопоставляться со столбцом в таблице реляционной базы данных. Далее в этом учебнике вы просматриваете данные о продуктах, содержащиеся в базе данных.

Заметки к данным

Вы могли заметить, что некоторые члены классов имеют атрибуты, указывающие сведения об элементе, например [ScaffoldColumn(false)]. Это заметки к данным. Атрибуты аннотации данных могут описывать, как проверить вводимые пользователем данные для этого элемента, задать для него форматирование и указать, как он моделируется при создании базы данных.

Класс Context

Чтобы приступить к использованию классов для доступа к данным, необходимо определить класс контекста. Как упоминалось ранее, класс контекста управляет классами сущностей (например, классом Product и классом Category) и предоставляет доступ к базе данных.

Эта процедура добавляет новый C# класс контекста в папку Models .

  1. Щелкните правой кнопкой мыши папку Models и выберите Добавить -> новый элемент.
    Откроется диалоговое окно Добавление нового элемента .

  2. Выберите класс в средней области, назовите его ProductContext.CS и нажмите кнопку добавить.

  3. Замените код по умолчанию, содержащийся в классе, следующим кодом:

    using System.Data.Entity;
    namespace WingtipToys.Models
    {
        public class ProductContext : DbContext
        {
            public ProductContext() : base("WingtipToys")
            {
            }
            public DbSet<Category> Categories { get; set; }
            public DbSet<Product> Products { get; set; }
        }
    }
    

Этот код добавляет пространство имен System.Data.Entity, чтобы иметь доступ ко всем основным функциональным возможностям Entity Framework, включая возможность запросов, вставки, обновления и удаления данных путем работы со строго типизированными объектами.

Класс ProductContext представляет контекст базы данных продукта Entity Framework, который обрабатывает выборку, хранение и обновление экземпляров класса Product в базе данных. Класс ProductContext является производным от базового класса DbContext, предоставляемого Entity Framework.

Класс инициализатора

Для инициализации базы данных при первом использовании контекста необходимо выполнить определенную пользовательскую логику. Это позволит добавить начальные данные в базу данных, чтобы можно было сразу же отображать продукты и категории.

Эта процедура добавляет новый C# класс инициализатора в папку Models .

  1. Создайте еще одну Class в папке Models и назовите ее ProductDatabaseInitializer.CS.

  2. Замените код по умолчанию, содержащийся в классе, следующим кодом:

    using System.Collections.Generic;
    using System.Data.Entity;
    
    namespace WingtipToys.Models
    {
      public class ProductDatabaseInitializer : DropCreateDatabaseIfModelChanges<ProductContext>
      {
        protected override void Seed(ProductContext context)
        {
          GetCategories().ForEach(c => context.Categories.Add(c));
          GetProducts().ForEach(p => context.Products.Add(p));
        }
    
        private static List<Category> GetCategories()
        {
          var categories = new List<Category> {
                    new Category
                    {
                        CategoryID = 1,
                        CategoryName = "Cars"
                    },
                    new Category
                    {
                        CategoryID = 2,
                        CategoryName = "Planes"
                    },
                    new Category
                    {
                        CategoryID = 3,
                        CategoryName = "Trucks"
                    },
                    new Category
                    {
                        CategoryID = 4,
                        CategoryName = "Boats"
                    },
                    new Category
                    {
                        CategoryID = 5,
                        CategoryName = "Rockets"
                    },
                };
    
          return categories;
        }
    
        private static List<Product> GetProducts()
        {
          var products = new List<Product> {
                    new Product
                    {
                        ProductID = 1,
                        ProductName = "Convertible Car",
                        Description = "This convertible car is fast! The engine is powered by a neutrino based battery (not included)." + 
                                      "Power it up and let it go!", 
                        ImagePath="carconvert.png",
                        UnitPrice = 22.50,
                        CategoryID = 1
                   },
                    new Product 
                    {
                        ProductID = 2,
                        ProductName = "Old-time Car",
                        Description = "There's nothing old about this toy car, except it's looks. Compatible with other old toy cars.",
                        ImagePath="carearly.png",
                        UnitPrice = 15.95,
                         CategoryID = 1
                   },
                    new Product
                    {
                        ProductID = 3,
                        ProductName = "Fast Car",
                        Description = "Yes this car is fast, but it also floats in water.",
                        ImagePath="carfast.png",
                        UnitPrice = 32.99,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 4,
                        ProductName = "Super Fast Car",
                        Description = "Use this super fast car to entertain guests. Lights and doors work!",
                        ImagePath="carfaster.png",
                        UnitPrice = 8.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 5,
                        ProductName = "Old Style Racer",
                        Description = "This old style racer can fly (with user assistance). Gravity controls flight duration." + 
                                      "No batteries required.",
                        ImagePath="carracer.png",
                        UnitPrice = 34.95,
                        CategoryID = 1
                    },
                    new Product
                    {
                        ProductID = 6,
                        ProductName = "Ace Plane",
                        Description = "Authentic airplane toy. Features realistic color and details.",
                        ImagePath="planeace.png",
                        UnitPrice = 95.00,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 7,
                        ProductName = "Glider",
                        Description = "This fun glider is made from real balsa wood. Some assembly required.",
                        ImagePath="planeglider.png",
                        UnitPrice = 4.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 8,
                        ProductName = "Paper Plane",
                        Description = "This paper plane is like no other paper plane. Some folding required.",
                        ImagePath="planepaper.png",
                        UnitPrice = 2.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 9,
                        ProductName = "Propeller Plane",
                        Description = "Rubber band powered plane features two wheels.",
                        ImagePath="planeprop.png",
                        UnitPrice = 32.95,
                        CategoryID = 2
                    },
                    new Product
                    {
                        ProductID = 10,
                        ProductName = "Early Truck",
                        Description = "This toy truck has a real gas powered engine. Requires regular tune ups.",
                        ImagePath="truckearly.png",
                        UnitPrice = 15.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 11,
                        ProductName = "Fire Truck",
                        Description = "You will have endless fun with this one quarter sized fire truck.",
                        ImagePath="truckfire.png",
                        UnitPrice = 26.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 12,
                        ProductName = "Big Truck",
                        Description = "This fun toy truck can be used to tow other trucks that are not as big.",
                        ImagePath="truckbig.png",
                        UnitPrice = 29.00,
                        CategoryID = 3
                    },
                    new Product
                    {
                        ProductID = 13,
                        ProductName = "Big Ship",
                        Description = "Is it a boat or a ship. Let this floating vehicle decide by using its " + 
                                      "artifically intelligent computer brain!",
                        ImagePath="boatbig.png",
                        UnitPrice = 95.00,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 14,
                        ProductName = "Paper Boat",
                        Description = "Floating fun for all! This toy boat can be assembled in seconds. Floats for minutes!" + 
                                      "Some folding required.",
                        ImagePath="boatpaper.png",
                        UnitPrice = 4.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 15,
                        ProductName = "Sail Boat",
                        Description = "Put this fun toy sail boat in the water and let it go!",
                        ImagePath="boatsail.png",
                        UnitPrice = 42.95,
                        CategoryID = 4
                    },
                    new Product
                    {
                        ProductID = 16,
                        ProductName = "Rocket",
                        Description = "This fun rocket will travel up to a height of 200 feet.",
                        ImagePath="rocket.png",
                        UnitPrice = 122.95,
                        CategoryID = 5
                    }
                };
    
          return products;
        }
      }
    }
    

Как видно из приведенного выше кода, при создании и инициализации базы данных свойство Seed переопределяется и задается. Если задано свойство Seed, для заполнения базы данных используются значения из категорий и продуктов. При попытке обновить данные начального значения путем изменения приведенного выше кода после создания базы данных никакие обновления не будут отображаться при запуске веб-приложения. Причина в том, что приведенный выше код использует реализацию класса DropCreateDatabaseIfModelChanges, чтобы распознать, изменилась ли модель (схема) перед сбросом начальных данных. Если Category и Product классы сущностей не были изменены, то база данных не будет повторно инициализирована с начальными данными.

Note

Если требуется, чтобы база данных была создана заново при каждом запуске приложения, можно использовать класс DropCreateDatabaseAlways вместо класса DropCreateDatabaseIfModelChanges. Однако в этой серии руководств используется класс DropCreateDatabaseIfModelChanges.

На этом этапе в этом учебнике у вас будет папка Models с четырьмя новыми классами и одним классом по умолчанию:

Создание уровня доступа к данным — папка "модели"

Настройка приложения для использования модели данных

Теперь, когда вы создали классы, представляющие данные, необходимо настроить приложение для использования классов. В файле Global. asax добавляется код, который инициализирует модель. В файле Web. config добавьте сведения, указывающие приложению, какая база данных будет использоваться для хранения данных, представленных новыми классами данных. Файл Global. asax можно использовать для управления событиями или методами приложения. Файл Web. config позволяет управлять конфигурацией веб-приложения ASP.NET.

Обновление файла Global. asax

Чтобы инициализировать модели данных при запуске приложения, необходимо обновить обработчик Application_Start в файле Global.asax.CS .

Note

В обозреватель решений можно выбрать либо файл Global. asax , либо файл Global.asax.CS , чтобы изменить файл Global.asax.CS .

  1. Добавьте следующий код, выделенный желтым цветом, в метод Application_Start в файле Global.asax.CS .

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.SessionState;
    using System.Data.Entity;
    using WingtipToys.Models;
    
    namespace WingtipToys
    {
        public class Global : HttpApplication
        {
            void Application_Start(object sender, EventArgs e)
            {
                // Code that runs on application startup
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
    
                // Initialize the product database.
                Database.SetInitializer(new ProductDatabaseInitializer());
            }
        }
    }
    

Note

Браузер должен поддерживать HTML5 для просмотра кода, выделенного желтым цветом, при просмотре этой серии руководств в браузере.

Как показано в приведенном выше коде, при запуске приложения в приложении указывается инициализатор, который будет выполняться во время первого обращения к данным. Для доступа к объекту Database и объекту ProductDatabaseInitializer требуются два дополнительных пространства имен.

Изменение файла Web. config

Несмотря на то, что Entity Framework Code First создаст базу данных в расположении по умолчанию, когда база данных заполняется начальными данными, добавление в приложение собственных сведений о подключении позволяет управлять расположением базы данных. Это подключение к базе данных указывается с помощью строки подключения в файле Web. config приложения в корне проекта. Добавив новую строку подключения, можно указать расположение базы данных (wingtiptoys. mdf), которая должна быть создана в каталоге данных приложения (Данные приложения_ ), а не его расположение по умолчанию. Внесение этого изменения позволит найти и проверить файл базы данных далее в этом руководстве.

  1. В Обозреватель решенийнайдите и откройте файл Web. config .

  2. Добавьте строку подключения, выделенную желтым цветом, в раздел <connectionStrings> файла Web. config следующим образом:

    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-WingtipToys-20131119102907.mdf;Initial Catalog=aspnet-WingtipToys-20131119102907;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    <add name="WingtipToys"
    connectionString="Data Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\wingtiptoys.mdf;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    </connectionStrings>
    

При первом запуске приложения будет построена база данных в расположении, указанном в строке подключения. Но перед запуском приложения создадим его первым.

Построение приложения

Чтобы убедиться, что все классы и изменения в веб-приложении работают, следует создать приложение.

  1. В меню Отладка выберите команду построить WingtipToys.
    Появится окно вывод, и если все прошло успешно, появится сообщение об успешной отводе .

    Создание уровня доступа к данным — окна вывода

При возникновении ошибки повторно проверьте указанные выше шаги. Информация в окне вывод указывает на то, какой файл имеет ошибку, и в каком файле требуется изменение. Эта информация позволит вам определить, какую часть описанных действий необходимо проверить и исправить в проекте.

Сводка

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

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

Дополнительные ресурсы

Обзор Entity Framework
Руководство для начинающих по Entity Framework ADO.NET
Разработка Code First с помощью Entity Framework (видео)
Code First связей API Fluent
Заметки к данным Code First
Улучшения производительности для Entity Framework