W poprzednim artykule zainstalowaliśmy oraz stworzyliśmy pierwszą aplikację webową z wykorzystaniem frameworka .NET Core. Obecnie zajmiemy się zaimple

W poprzednim artykule zainstalowaliśmy oraz stworzyliśmy pierwszą aplikację webową z wykorzystaniem frameworka .NET Core. Obecnie zajmiemy się zaimplementowaniem prostej aplikacji CRUD pracującej na silniku MS SQL Server zainstalowanym w systemie Linux.

 

Tworzenie bazy danych

 

Zanim przejdziemy do właściwej implementacji, przygotujemy bazę danych MS SQL pod aplikację. Na początek logujemy się do serwera, korzystając z polecenia:


sqlcmd -S localhost -U sa

 

Następnie podajemy hasło administratora (identyczne, jakie wpisaliśmy przy konfiguracji serwera).

Kolejno tworzymy bazę danych i przechodzimy do utworzonej bazy poleceniami:


create database localshop; go use localshop; go

 

W następnym kroku tworzymy sql login oraz użytkownika poleceniami:

 

 create login shopus with password 'Local123!@'; go create user shopus from login shopus; go 

 

 

Na samym końcu nowo utworzonemu użytkownikowi nadajemy uprawnienia dostępu do naszej bazy danych:

 

 exec sp_addrolemember 'db_owner', 'shopus'; go 

 

Mamy utworzoną bazę danych i użytkownika, poprzez którego będziemy łączyć się z bazą i wykonywać różne operacje na danych w tabelach.  

 

Naszym celem jest implementacja prostej aplikacji webowej do zarządzania magazynem małego osiedlowego sklepu. Zatem będzie to prosta aplikacja CRUD (Create, Read, Update, Delete – podstawowe operacje wykonywane na tabelach bazy danych).  

 

Krok I (utworzenie connectionString)

 

Otwieramy plik appsettings.json i wpisujemy:


"ConnectionStrings": {
"DefaultConnection": "Server=tcp:localhost,1433;Database=localshop;User Id=shopus; Password = Local123!@" },

 

Nasz connectionString pozwala na podłączenie się do lokalnego serwera MS SQL nasłuchującego na standardowym porcie TCP 1433 do bazy danych o nazwie localshop, korzystając z użytkownika shopus o podanym haśle do logowania.  

 

Krok II (utworzenie klas modelu)

 

Tworzymy katalog Models w głównym folderze projektu. Models będzie zawierał wszystkie klasy modelu aplikacji. Następnie tworzymy plik Product.cs (klasę opisującą pojedynczy produkt w sklepie) i implementujemy klasę, korzystając z właściwości:

 

 public class Product { public int ID { get; set; } // ID produktu public string Name { get; set; } // nazwa public string EAN { get; set; } // kod kreskowy public double Price { get; set; } // cena jednostkowa public string Unit { get; set; } // podstawowa jednostka public int Availability { get; set; } // stan magazynowy } 

 

 

 

Krok III (utworzenie klasy Database Context) Tworzymy nowy katalog Data w folderze głównym aplikacji, a w nowym katalogu tworzymy plik ProductContext.cs, który będzie zapewniał interakcję aplikacji z danymi w bazie danych:

 

 using ProductsAspNetCore.Models; using Microsoft.EntityFrameworkCore; public class ProductContext: DbContext { public ProductContext(DbContextOptions options): base(options) { } public DbSet Products { get; set; } } }  

 

 

W tym momencie Visual Studio Code wyświetli błąd o braku dostępnych bibliotek do rozpoznania importowanej przestrzeni nazw EntityFrameworkCore. Musimy przejść do pliku ProductsAspNetCore.csproj i w sekcji dodać wpis:

  />

 

 

Następnie wywołujemy polecenie dotnet restore, które pobierze i zainstaluje brakujące paczki w naszym projekcie.  

 

Krok IV (rejestracja ProductContext jako usługi w aplikacji)

 

Otwieramy plik Startup.cs i w metodzie ConfigureServices, przed services.AddMvc(); dodajemy wpis:

 services.AddDbContext(options=>options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection")) );  

 

 

Na początku pliku zaś dodajemy deklarację użycia:

 using ProductsAspNetCore.Data; using Microsoft.EntityFrameworkCore;    

 

 

Krok V (implementacja klasy inicjalizującej bazę danych przykładowymi wartościami)

 

Tworzymy plik DbInitialize.cs w katalogu Data, który zainicjalizuje tabele bazy danych przykładowymi wartościami w trakcie pierwszego uruchomienia aplikacji. Tworzymy statyczną klasę DbInitialize wraz ze statyczną metodą Initialize:

 public static class DbInitialize { public static void Initialize(ProductContext context) { context.Database.EnsureCreated(); if(context.Products.Any()){ // sprawdzenie, czy baza zawiera jakiekolwiek dane // wyjście z funkcji w przypadku wykrycia już danych w bazie } var products=new List(); // lista przykładowych danych (produktów) products.Add(new Product{ Name="Chleb zwykły", EAN="569856325698", Price=2.65, Unit="sztuka", Availability=15 }); products.Add(new Product{ Name="Bułka codzienna", EAN="653789512364", Price=0.35, Unit="sztuka", Availability=250 }); products.Add(new Product{ Name="Mleko wiejskie", EAN="986321476589", Price=4.98, Unit="litr", Availability=30 }); foreach(Product p in products) { context.Products.Add(p); } context.SaveChanges(); // zapis przykładowych danych do bazy } } 

 

 

 

Krok VI (wywołanie metody inicjalizującej bazę danych)

 

Otwieramy ponownie plik Startup.cs i w nagłówku metody Configure dodajemy nowy parametr: ProductContext context. Natomiast na końcu dopisujemy wywołanie metody z kroku V:

 DbInitialize.Initialize(context); 

 

 

 

Krok VII (uruchomienie aplikacji)

 

Uruchamiamy aplikację poleceniem dotnet run. Logujemy się do bazy danych MS SQL i sprawdzamy, czy przykładowe dane zostały wpisane do bazy.

Jak widać, wszystko przebiegło pomyślnie.  

 

Krok VIII (wyświetlenie produktów z bazy danych)

 

W folderze Controllers tworzymy nowy plik o nazwie ProductsController.cs, który będzie zawierał klasę kontrolera obsługującą wszystkie żądania HTTP do aplikacji odnośnie produktów. W pliku implementujemy klasę dziedziczącą z klasy Controller:

 public class ProductsController : Controller { } 

 

 

Następnie do klasy dodajemy prywatne pole typu ProductContext wraz konstruktorem klasy inicjalizującym pole poprzez mechanizm wstrzykiwania zależności (ang. Dependency Injection):

 private readonly ProductContext context; public ProductsController(ProductContext _context) { context=_context; } 

 

 

 

Aby wyświetlić listę produktów, musimy dodać metodę zwracającą widok wypełniony danymi z bazy danych:

 public IActionResult Index() { return View(context.Products.ToList()); // zwraca widok wraz z listą wszystkich produktów } 

 

 

 

Na samym końcu musimy stworzyć widok, który pozwoli wyświetlić dane zwrócone w powyższej metodzie. W katalogu Views tworzymy nowy folder o identycznej nazwie jak nasz kontroler, tzn. Products. W nowym katalogu tworzymy plik o nazwie Index.cshtml (nazwa pliku widoku musi być identyczna z nazwą metody zwracającej dany widok). Kolejno do pliku Index.cshtml wpisujemy:

 @model IEnumerable // model danych wykorzystanych w widoku @{ ViewData["Title"]="Lista produktów"; // tytuł strony }               ............................................ @foreach (var item in Model) { // pętla przechodząca po wszystkich elementach listy produktów ........................................... }

 

@Html.DisplayNameFor(model => model.ID) // nagłówek kolumny @Html.DisplayNameFor(model => model.Name)
@Html.DisplayFor(modelItem => item.ID) // dane wyświetlane z bazy, ID produktu @Html.DisplayFor(modelItem => item.Name) // nazwa produktu

 

 

 

 

Przechodzimy do przeglądarki, wpisujemy adres http://localhost:5000/Products i otrzymujemy listę wszystkich produktów.  

 

Jak widać, nazwy kolumn są identyczne z nazwami pól klasy modelu Product. Jednak można zdefiniować własne wyświetlane nazwy, korzystając z DataAnnotations. Przechodzimy do klasy Product i dodajemy na początku pliku przestrzenie nazw odpowiedzialne za dodatkowe atrybuty pól:

 

 using System.ComponentModel; using System.ComponentModel.DataAnnotations; 

 

 

Kolejno przy każdym polu dodajemy sekcję [DisplayName(„”)], np.:

 

 [DisplayName("Kod kreskowy EAN")] public string EAN { get; set; } 

 

 

Po zdefiniowaniu własnych nazw kolumn tabela wyświetla się następująco:  

 

Krok IX (dodawanie nowego produktu)

 

Na początku w pliku głównym widoku Index.cshtml dodajemy link kierujący do formularza definiowania nowego produktu:

 

 Dodaj nowy produkt 

 

Tag asp-action odpowiada za wskazanie metody, która będzie wywołana z kontrolera Products po kliknięciu w link.  

 

Kolejno w katalogu Products tworzymy widok wyświetlający formularz dodawania produktu (plik Create.cshtml). W pliku definiujemy formularz:

 

 @model ProductsAspNetCore.Models.Product // model danych

 

 

Nazwa: // pole tekstowe o nazwie Name Kod kreskowy: // pole tekstowe o nazwie EAN Cena jednostkowa: // pole tekstowe o nazwie Price       ........................................................ // przycisk formularza

 

 

 

Tag asp-controller wskazuje, do którego kontrolera odnosi się dany formularz. Natomiast asp-action — która metoda ma być wywoływana z kontrolera w trakcie przetwarzania formularza.   Następnie przechodzimy do kontrolera ProductsController i definiujemy dwie metody:

 

  • wyświetlenie formularza:

 

 public IActionResult Create() { return View(); } 

 

 

  • obsługa formularza:

 

  [HttpPost] // wskazuje, że dana akcja obsługunt Availability { get; set5dania POST public IActionResult Create(Product product) // jako parametr otrzymuje obiekt zwrócony z formularza { context.Products.Add(product); // dodanie produktu z danymi z widoku do Product DbSet context.SaveChanges(); // zapisanie produktu w bazie danych return RedirectToAction("Index"); // przekierowanie do widoku Index } 

   

 

 

Krok X (edycja wybranego towaru)

 

Na początku w pliku głównym widoku Index.cshtml dodajemy link do formularza edycji wybranego produktu:

  Edycja 

  Z kolei w katalogu Products tworzymy widok wyświetlający formularz edycji wybranego produktu (plik Edit.cshtml). W pliku definiujemy formularz:

  @model ProductsAspNetCore.Models.Product // model danych

 

Nazwa: Kod kreskowy: Cena jednostkowa: Jednostka: Stan magazynowy:  

 

 

 

 

Formularz będzie obsługiwany przez akcję Edit z kontrolera Products (opisane w tagach asp-controller i asp-action).  

 

Następnie przechodzimy do kontrolera ProductsController i definiujemy dwie metody:

 

  • wyświetlenie formularza edycyjnego:

 

  public IActionResult Edit(int id) { Product product=context.Products.Find(id); // wyszukiwanie produktu o podanym ID return View(product); // zwrócenie wyszukanego produktu do widoku } 

 

 

  • obsługa formularza, aktualizacja danych produktu:

 

  [HttpPost] // wskazuje, że dana akcja obsługuje przychodzące żądania POST public IActionResult Edit(Product product) // jako parametr pobiera obiekt zwrócony z formularza { context.Update(product); // aktualizacja danych produktu w Product DbSet context.SaveChanges(); // zapisanie zmian do bazy danych return RedirectToAction("Index"); // przekierowanie do głównego widoku Index } 

 

 

 

Krok XI (usuwanie wybranego towaru)

 

W pliku głównego widoku Index.cshtml dodajemy link pozwalający usunąć wybrany produkt:

 

  ..................................... Usuń 

 

 

 

Kolejno w kontrolerze ProductsController tworzymy metodę usuwającą wybrany produkt:

 

  public IActionResult Delete(int id) { Product product=context.Products.Find(id); // wyszukanie produktu o zadanym ID context.Remove(product); // usunięcie produktu z Product DbSet context.SaveChanges(); // zapisanie zmian do bazy danych return RedirectToAction("Index"); // przekierowanie do głównego widoku Index } 

 

 

 

W ten oto sposób napisaliśmy prostą aplikację webową z wykorzystaniem ASP .NET Core, którą później można hostować w systemie Windows, Linux oraz Mac.  

 

Kompletny projekt możecie podejrzeć na GitHubie TUTAJ.  

Krzysztof Goljasz

devgeek.pl