Tworzenie warstwy dostępu do danych

Autor: Erik Reitan

Ta seria samouczków zawiera podstawowe informacje dotyczące tworzenia aplikacji ASP.NET Web Forms przy użyciu ASP.NET 4.5 i Microsoft Visual Studio Express 2013 for Web. Dostępny jest projekt Visual Studio 2013 z kodem źródłowym języka C#, który towarzyszy tej serii samouczków.

W tym samouczku opisano sposób tworzenia, uzyskiwania dostępu i przeglądania danych z bazy danych przy użyciu ASP.NET Web Forms i Entity Framework Code First. Ten samouczek jest oparty na poprzednim samouczku "Tworzenie projektu" i jest częścią serii samouczków Wingtip Toy Store. Po ukończeniu tego samouczka utworzysz grupę klas dostępu do danych, które znajdują się w folderze Models projektu.

Zawartość:

  • Jak tworzyć modele danych.
  • Jak zainicjować i zainicjować bazę danych.
  • Jak zaktualizować i skonfigurować aplikację do obsługi bazy danych.

Są to funkcje wprowadzone w samouczku:

  • Entity Framework Code First
  • LocalDB
  • Adnotacje danych

Tworzenie modeli danych

Entity Framework to struktura mapowania obiektowo-relacyjnego (ORM). Umożliwia ona pracę z danymi relacyjnymi jako obiektami, eliminując większość kodu dostępu do danych, który zwykle trzeba będzie napisać. Korzystając z platformy Entity Framework, można wysyłać zapytania przy użyciu LINQ, a następnie pobierać dane i manipulować nimi jako silnie typizowane obiekty. LINQ udostępnia wzorce do wykonywania zapytań i aktualizowania danych. Korzystanie z programu Entity Framework umożliwia skoncentrowanie się na tworzeniu pozostałej części aplikacji, a nie skupieniu się na podstawach dostępu do danych. W dalszej części tej serii samouczków pokażemy, jak używać danych do wypełniania zapytań dotyczących nawigacji i produktów.

Platforma Entity Framework obsługuje paradygmat programowania o nazwie Code First. Funkcja Code First umożliwia definiowanie modeli danych przy użyciu klas. Klasa to konstrukcja, która umożliwia tworzenie własnych typów niestandardowych przez grupowanie zmiennych innych typów, metod i zdarzeń. Klasy można mapować na istniejącą bazę danych lub użyć ich do wygenerowania bazy danych. W tym samouczku utworzysz modele danych, zapisując klasy modelu danych. Następnie pozwolisz programowi Entity Framework utworzyć bazę danych na bieżąco z tych nowych klas.

Zaczniesz od utworzenia klas jednostek, które definiują modele danych dla aplikacji Web Forms. Następnie utworzysz klasę kontekstu, która zarządza klasami jednostek i zapewnia dostęp do danych do bazy danych. Utworzysz również klasę inicjatora, która będzie używana do wypełniania bazy danych.

Entity Framework i odwołania

Domyślnie program Entity Framework jest dołączany podczas tworzenia nowej aplikacji internetowej ASP.NET przy użyciu szablonu Web Forms. Program Entity Framework można zainstalować, odinstalować i zaktualizować jako pakiet NuGet.

Ten pakiet NuGet zawiera następujące zestawy środowiska uruchomieniowego w projekcie:

  • EntityFramework.dll — cały wspólny kod środowiska uruchomieniowego używany przez platformę Entity Framework
  • EntityFramework.SqlServer.dll — dostawca SQL Server firmy Microsoft dla programu Entity Framework

Klasy jednostek

Klasy tworzone w celu zdefiniowania schematu danych są nazywane klasami jednostek. Jeśli jesteś nowym użytkownikiem projektu bazy danych, pomyśl o klasach jednostek jako definicjach tabel bazy danych. Każda właściwość w klasie określa kolumnę w tabeli bazy danych. Te klasy zapewniają lekki, obiektowo-relacyjny interfejs między kodem obiektowym a strukturą tabeli relacyjnej bazy danych.

W tym samouczku zaczniesz od dodania prostych klas jednostek reprezentujących schematy produktów i kategorii. Klasa products będzie zawierać definicje dla każdego produktu. Nazwa każdej z składowych klasy produktu to ProductID, ProductName, Description, ImagePath, UnitPrice, CategoryID, i Category. Klasa kategorii będzie zawierać definicje dla każdej kategorii, do których może należeć produkt, na przykład Samochód, Łódź lub Płaszczyzna. Nazwa każdej z składowych klasy kategorii to CategoryID, CategoryName, Descriptioni Products. Każdy produkt będzie należeć do jednej z kategorii. Te klasy jednostek zostaną dodane do istniejącego folderu Models projektu.

  1. W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Models, a następnie wybierz polecenie Dodaj ->Nowy element.

    Zrzut ekranu przedstawiający okno Eksplorator rozwiązań z wyróżnionym folderem Models oraz wybranymi menu rozwijanymi Dodaj i Nowy element.

    Zostanie wyświetlone okno dialogowe Dodaj nowy element.

  2. W obszarze Visual C# w okienku Zainstalowane po lewej stronie wybierz pozycję Kod.

    Zrzut ekranu przedstawiający okno Dodawanie nowego elementu z okienkiem Zainstalowane po lewej stronie z wybraną pozycją Otwórz program Visual C# i wybierz pozycję Kod.

  3. Wybierz pozycję Klasa w środkowym okienku i nadaj tej nowej klasie nazwę Product.cs.

  4. Kliknij pozycję Dodaj.
    Nowy plik klasy jest wyświetlany w edytorze.

  5. Zastąp kod domyślny następującym kodem:

    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. Utwórz kolejną klasę, powtarzając kroki od 1 do 4, jednak nadaj nowej klasie nazwę Category.cs i zastąp kod domyślny następującym kodem:

    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; }
        }
    }
    

Jak wspomniano wcześniej, klasa reprezentuje typ produktu, Category który aplikacja jest przeznaczona do sprzedaży (na przykład "Samochody", "Łodzie", "Rakiety" itd.), a Product klasa reprezentuje poszczególne produkty (zabawki) w bazie danych. Każde wystąpienie Product obiektu będzie odpowiadać wierszowi w tabeli relacyjnej bazy danych, a każda właściwość klasy Product będzie mapować na kolumnę w tabeli relacyjnej bazy danych. W dalszej części tego samouczka zapoznasz się z danymi produktu zawartymi w bazie danych.

Adnotacje danych

Być może zauważyliśmy, że niektóre elementy członkowskie klas mają atrybuty określające szczegóły dotyczące składowej, takie jak [ScaffoldColumn(false)]. Są to adnotacje danych. Atrybuty adnotacji danych mogą opisywać sposób weryfikowania danych wejściowych użytkownika dla tego elementu członkowskiego, określania jego formatowania oraz określania sposobu modelowania podczas tworzenia bazy danych.

Context — Klasa

Aby rozpocząć korzystanie z klas dostępu do danych, należy zdefiniować klasę kontekstu. Jak wspomniano wcześniej, klasa kontekstu zarządza klasami jednostek (takimi jak Product klasa i Category klasa) i zapewnia dostęp do danych do bazy danych.

Ta procedura dodaje nową klasę kontekstu języka C# do folderu Models .

  1. Kliknij prawym przyciskiem myszy folder Models , a następnie wybierz polecenie Dodaj ->Nowy element.
    Zostanie wyświetlone okno dialogowe Dodaj nowy element.

  2. Wybierz pozycję Klasa w środkowym okienku, nadaj jej nazwę ProductContext.cs i kliknij przycisk Dodaj.

  3. Zastąp domyślny kod zawarty w klasie następującym kodem:

    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; }
        }
    }
    

Ten kod dodaje System.Data.Entity przestrzeń nazw, aby mieć dostęp do wszystkich podstawowych funkcji platformy Entity Framework, która obejmuje możliwość wykonywania zapytań, wstawiania, aktualizowania i usuwania danych przez pracę z obiektami silnie typizowanym.

Klasa ProductContext reprezentuje kontekst bazy danych produktu Entity Framework, który obsługuje pobieranie, przechowywanie i aktualizowanie Product wystąpień klas w bazie danych. Klasa ProductContext pochodzi z klasy bazowej dostarczonej DbContext przez program Entity Framework.

Initializer, klasa

Aby zainicjować bazę danych przy pierwszym użyciu kontekstu, należy uruchomić logikę niestandardową. Pozwoli to na dodanie danych inicjujących do bazy danych, dzięki czemu można natychmiast wyświetlać produkty i kategorie.

Ta procedura dodaje nową klasę inicjatora języka C# do folderu Models .

  1. Utwórz inny Class element w folderze Models i nadaj mu nazwę ProductDatabaseInitializer.cs.

  2. Zastąp domyślny kod zawarty w klasie następującym kodem:

    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;
        }
      }
    }
    

Jak widać w powyższym kodzie, po utworzeniu i zainicjowaniu Seed bazy danych właściwość jest zastępowana i ustawiana. Po ustawieniu Seed właściwości wartości z kategorii i produktów są używane do wypełniania bazy danych. Jeśli spróbujesz zaktualizować dane inicjujące, modyfikując powyższy kod po utworzeniu bazy danych, nie zobaczysz żadnych aktualizacji podczas uruchamiania aplikacji internetowej. Przyczyną jest użycie powyższego kodu implementacji DropCreateDatabaseIfModelChanges klasy w celu rozpoznania, czy model (schemat) uległ zmianie przed zresetowaniem danych inicjujących. Jeśli w klasach jednostek i Product nie zostaną wprowadzone Category żadne zmiany, baza danych nie zostanie ponownie zainicjowana przy użyciu danych inicjalnych.

Uwaga

Jeśli chcesz, aby baza danych była odtwarzana za każdym razem, gdy uruchamiasz aplikację, możesz użyć DropCreateDatabaseAlways klasy zamiast DropCreateDatabaseIfModelChanges klasy . Jednak w tej serii samouczków użyj DropCreateDatabaseIfModelChanges klasy .

Na tym etapie tego samouczka będziesz mieć folder Models z czterema nowymi klasami i jedną klasą domyślną:

Tworzenie folderu Warstwa dostępu do danych — modele

Konfigurowanie aplikacji do korzystania z modelu danych

Po utworzeniu klas reprezentujących dane należy skonfigurować aplikację do używania klas. W pliku Global.asax dodasz kod, który inicjuje model. W pliku Web.config dodasz informacje, które informują aplikację, której bazy danych użyjesz do przechowywania danych reprezentowanych przez nowe klasy danych. Plik Global.asax może służyć do obsługi zdarzeń lub metod aplikacji. Plik Web.config umożliwia kontrolowanie konfiguracji aplikacji internetowej ASP.NET.

Aktualizowanie pliku Global.asax

Aby zainicjować modele danych podczas uruchamiania aplikacji, zaktualizujesz Application_Start program obsługi w pliku Global.asax.cs .

Uwaga

W Eksplorator rozwiązań możesz wybrać plik Global.asax lub plik Global.asax.cs, aby edytować plik Global.asax.cs.

  1. Dodaj następujący kod wyróżniony kolorem żółtym Application_Start do metody w pliku 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());
            }
        }
    }
    

Uwaga

Przeglądarka musi obsługiwać kod HTML5, aby wyświetlić kod wyróżniony na żółto podczas wyświetlania tej serii samouczków w przeglądarce.

Jak pokazano w powyższym kodzie, po uruchomieniu aplikacji aplikacja określa inicjator, który będzie uruchamiany podczas pierwszego uzyskiwania dostępu do danych. Dwa dodatkowe przestrzenie nazw są wymagane do uzyskania dostępu Database do obiektu i ProductDatabaseInitializer obiektu.

Modyfikowanie pliku Web.Config

Mimo że program Entity Framework Code First wygeneruje bazę danych w domyślnej lokalizacji, gdy baza danych jest wypełniana danymi inicjanymi, dodanie własnych informacji o połączeniu do aplikacji daje kontrolę nad lokalizacją bazy danych. To połączenie z bazą danych określa się przy użyciu parametrów połączenia w pliku Web.config aplikacji w katalogu głównym projektu. Dodając nowe parametry połączenia, można skierować lokalizację bazy danych (wingtiptoys.mdf) do skompilowania w katalogu danych aplikacji (App_Data) zamiast lokalizacji domyślnej. Wprowadzenie tej zmiany umożliwi znalezienie i sprawdzenie pliku bazy danych w dalszej części tego samouczka.

  1. W Eksplorator rozwiązań znajdź i otwórz plik Web.config.

  2. Dodaj parametry połączenia wyróżnione kolorem żółtym do <connectionStrings> sekcji pliku Web.config w następujący sposób:

    <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;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)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\wingtiptoys.mdf;Integrated Security=True"
    providerName="System.Data.SqlClient" />
    </connectionStrings>
    

Gdy aplikacja zostanie uruchomiona po raz pierwszy, skompiluje bazę danych w lokalizacji określonej przez parametry połączenia. Ale przed uruchomieniem aplikacji skompilujmy ją najpierw.

Kompilowanie aplikacji

Aby upewnić się, że wszystkie klasy i zmiany aplikacji internetowej działają, należy skompilować aplikację.

  1. Z menu Debuguj wybierz pozycję Kompiluj WingtipToys.
    Zostanie wyświetlone okno Dane wyjściowe . Jeśli wszystko poszło dobrze, zostanie wyświetlony komunikat o powodzeniu .

    Tworzenie warstwy dostępu do danych — okna wyjściowe

Jeśli wystąpi błąd, sprawdź ponownie powyższe kroki. Informacje w oknie Dane wyjściowe wskazują, który plik ma problem i gdzie w pliku jest wymagana zmiana. Te informacje umożliwią określenie, która część powyższych kroków należy przejrzeć i naprawić w projekcie.

Podsumowanie

W tym samouczku serii utworzono model danych, a także dodano kod, który będzie używany do inicjowania i rozmieszczania bazy danych. Skonfigurowano również aplikację do używania modeli danych podczas uruchamiania aplikacji.

W następnym samouczku zaktualizujesz interfejs użytkownika, dodasz nawigację i pobierzesz dane z bazy danych. Spowoduje to automatyczne utworzenie bazy danych na podstawie klas jednostek utworzonych w tym samouczku.

Dodatkowe zasoby

Omówienie programu Entity Framework
Przewodnik dla początkujących dotyczący platformy Entity Framework ADO.NET
Code First Development with Entity Framework Code FirstRelationships Fluent API
Adnotacje do danych Code First
Ulepszenia produktywności dla platformy Entity Framework