Wprowadzenie
Artykuł ten będzie poświęcony Entity Framework z perspektywy
osoby początkującej a przeznaczony przede wszystkim dla programistów używających na co dzień
ADO.NET jako warstwy dostępu do danych. Dla doświadocznych
programistów ten artykuł będzie bardzo prosty, gdyż wiedza tutaj przedstawiona została
przygotowana specjalnie dla osób początkujących.
ADO.NET jest bardzo dobrym narzędziem dostępu do danych.
Istnieje od wielu lat i wykorzystywanych jest w wielu systemach. Programista, który nie
miał nic wspólnego z ORM zapewne będzie chciał wiedzieć
czym jest Entity Framework? Jako są korzyści z jego używania
i czy jest alternatywą dla ADO.NET?
Odpowiedź na pierwsze pytanie: Entity Framework jest
narzędziem mapowania obiektowo-relacyjnego, tj. ORM.
Generuje obiekty biznesowe oraz encje zgodnie z
tabelami baz danych. Obiekt biznesowy jest encją (entity)
w wielowarstowej aplikacji, która działa w połączeniu z dostępem do bazy danych oraz wartwą
logiki biznesowej służącą do przesyłania danych. Z kolei nasze
entity odnosi się do czegoś co jest unikatowe i istnieje
oddzielnie, np. tabela bazy danych. Entity Framework pozwala na:
-
Wykonywanie podstawowych operacji CRUD
(Create, Read, Update, Delete).
- Łatwe zarządzanie relacjami 1 do 1, 1 do wielu oraz wiele do wielu.
- Tworzenie relacji dziedziczenia pomiędzy encjami.
Odpowiedzią na drugie pytanie są zalety używania tego rozwiązania:
- Cała logika dostępu do bazy danych może być napisana w języku wyższego poziomu.
-
Koncepcyjny model może zostać zaprezentowany w lepszy sposób za pomocą relacji pomiędzy
encjami.
-
Bazowe dane mogą być zastąpione bez większego narzutu ponieważ cała logika biznesowa
dostępu do danych jest na wyższym poziomie.
Odpowiedź na ostatnie pytanie nie jest jednoznaczna. Czy jest to alternatywa dla
ADO.NET? I takie i nie...
Tak, ponieważ programista nie będzie musiał pisał metod i klas do wykonywania operacji.
Nie, ponieważ model ten jest napisany w rzeczywistości w ADO.NET,
tzn. przy użyciu tej technologii, więc wciąż używamy ADO.NET.
Spójrzcie na poniższy diagram:
Konfiguracja
Postaramy się zrozumieć użycie Entity Framework dzięki
wykonywaniu prostych operacji CRUD. Jak tylko spojrzycie
na kod i zobaczycie z jaką łatwością możecie wykonywać te operacjie zrozumieć dlaczego
warto używać tej technologii.
Baza danych
W artykule będę używał tradycyjnej już bazy danych AdventureWorks udostępnionej przez
Microsoft. Możecie ją pobrać z poniższego linka ->
http://msftdbprodsamples.codeplex.com/
Model
Po pobraniu bazy danych możemy przystąpić do przygotowania modelu. Do naszego projektu
dodajemy ADO.NET Entity Data Model:
Następnie musimy wskazać sposób, którym chcemy się posłużyć tworząc nasz model:
Jak widzicie mamy do wyboru kilka możliwych podejść. W przypadku tego artykułu skupimy się
na pierwszym podejściu, utworzymy nasz model na podstawie istniejącej bazy danych. Jak tylko
model będzie gotowy utworzą się encje dla każdej z tabel. Encja dla tabeli
Employee wygląda w następujący sposób:
Ponadto, zostały również utworzone klasy, które pozwolą nam na wykonywanie operacji na
bazie danych. Pozostało nam tylko dowiedzieć się, w jaki sposób należy wykonywać operacje
na bazie danych.
Użycie kodu
Wszystko jest już gotowe do testowania. Wspomniana przeze mnie nieco wyżej automatycznie
utworzona klasa pozwalająca na wykonywanie operacji na bazie danych to
AdventureWorks2012_DataEntities. Przejdźmy do właściwych przykładów.
Pobieranie danych
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var employee = db.Employee;
// Wyświetlamy przykładowe dane w konsoli
foreach (var item in employee)
{
// Poniższy zapis to nowość w języku C# w wersji 6.0
// Poprzedni sposób formatowania tekstu
// Console.WriteLine("Id: {0}, Płeć: {1}, Data zatrudnienia: {2}", item.BusinessEntityID, item.Gender, item.HireDate)
Console.WriteLine($"Id: {item.BusinessEntityID}, Płeć: {item.Gender}, Data zatrudnienia: {item.HireDate}");
}
Gdybyśmy chcieli wyświetlić pracowników zatrudnionych w roku 2007 wystarczy niewielka modyfikacja:
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var employeeHiredIn2007 = db.Employee.Where(a => a.HireDate.Year == 2007);
// Wyświetlamy przykładowe dane w konsoli
foreach (var item in employeeHiredIn2007)
{
Console.WriteLine($"Id: {item.BusinessEntityID}, Płeć: {item.Gender}, Data zatrudnienia: {item.HireDate}");
}
A kolejną modyfikacją może być zwrócenie dokładnie jednego pracownika:
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
Employee spec = db.Employee.Single(a => a.BusinessEntityID == id);
Console.WriteLine("Wskazany pracownik: ");
Console.WriteLine($"Id: {spec.BusinessEntityID}, Płeć: {spec.Gender}, Data zatrudnienia: {spec.HireDate}");
Dodawanie danych
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// Definiujemy nowy obiekt do dodania
Department dp = new Department();
dp.GroupName = "Sample Group Name";
dp.ModifiedDate = DateTime.Now;
dp.Name = "Sample Name";
// Dodajemy obiekt do naszej tabeli
db.Department.Add(dp);
// Zapisujemy zmiany
db.SaveChanges();
Aktualizowanie danych
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// Do naszej metody przekazaliśmy id ostatniego elememtu
Department dp = db.Department.Single(a => a.DepartmentID == idToUpdate);
dp.Name = "Zaktualizowana nazwa departamentu";
// Wystarczy zapisać zmiany
db.SaveChanges();
Console.ReadKey();
Kasowanie danych
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// pobieramy element, który chcemy usunąć
Department dep = db.Department.Single(a => a.DepartmentID == getLastId);
// kasujemy wpis z tabeli
db.Department.Remove(dep);
// zapisujemy zmiany
db.SaveChanges();
Wywołanie procedury składowanej
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
foreach (var item in db.uspGetEmployeeManagers(3))
{
Console.WriteLine($"Id: {item.BusinessEntityID}, Imie: {item.FirstName}, Nazwisko: {item.LastName}");
}
Komplenty kod
using System;
using System.Linq;
namespace EntityFrameworkWprowadzenie
{
class Program
{
static void Main(string[] args)
{
NextAction();
}
public static void NextAction()
{
Console.WriteLine("Wybierz działanie, które chesz wykonać na bazie danych: ");
Console.WriteLine("1: Pobranie danych z tabeli Employee");
Console.WriteLine("2: Pobranie zatudnionych w roku 2007 z tabeli Employee");
Console.WriteLine("3: Pobranie wybranego pracownika");
Console.WriteLine("4: Dodanie nowego Departamentu");
Console.WriteLine("5: Aktualizacja nowo dodanego Departamentu");
Console.WriteLine("6: Pobranie danych z tabeli Department");
Console.WriteLine("7: Usunięcie ostatniego rekordu z tabeli Department");
Console.WriteLine("8: Opuścić aplikację: ");
Console.Write("Twój wybór: ");
int actionId = Convert.ToInt32(Console.ReadLine());
Console.WriteLine();
switch (actionId)
{
case 1:
GetAllData();
break;
case 2:
HiredIn2007();
break;
case 3:
SpecificEmployeeId(32);
break;
case 4:
AddNewDepartment();
break;
case 5:
int getLastId = GetLastAddedRow();
UpdateData(getLastId);
break;
case 6:
GetAllDataFromDepartment();
break;
case 7:
getLastId = GetLastAddedRow();
DeleteSpecifiedId(getLastId);
break;
case 8:
System.Environment.Exit(0);
break;
default:
break;
}
Console.ReadKey();
}
private static void DeleteSpecifiedId(int getLastId)
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// pobieramy element, który chcemy usunać
Department dep = db.Department.Single(a => a.DepartmentID == getLastId);
// kasujemy wpis z tabeli
db.Department.Remove(dep);
// zapisujemy zmiany
db.SaveChanges();
Console.WriteLine();
NextAction();
}
private static void GetAllDataFromDepartment()
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var dep = db.Department;
foreach (var item in dep)
{
Console.WriteLine($"Id: {item.DepartmentID}, Nazwa: {item.Name}, Nazwa grupy: {item.GroupName}");
}
Console.WriteLine();
NextAction();
}
private static void UpdateData(int idToUpdate)
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// Do naszej metody przekazaliśmy id ostatniego elememtu
Department dp = db.Department.Single(a => a.DepartmentID == idToUpdate);
dp.Name = "Zaktualizowana nazwa departamentu";
// Wystarczy zapisać zmiany
db.SaveChanges();
Console.WriteLine();
NextAction();
}
private static int GetLastAddedRow()
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var dep = db.Department.OrderByDescending(a => a.DepartmentID).FirstOrDefault();
// Console.WriteLine("Ostatni wiersz w tabeli Department: ");
// Console.WriteLine($"Id: {dep.DepartmentID}, Nazwa: {dep.Name}, Nazwa grupy: {dep.GroupName}");
return dep.DepartmentID;
}
private static void AddNewDepartment()
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
// Definiujemy nowy obiekt do dodania
Department dp = new Department();
dp.GroupName = "Sample Group Name";
dp.ModifiedDate = DateTime.Now;
dp.Name = "Sample Name";
// Dodajemy obiekt do naszej tabeli
db.Department.Add(dp);
// Zapisujemy zmiany
db.SaveChanges();
Console.WriteLine();
NextAction();
}
static void GetAllData()
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var employee = db.Employee;
// Wyświetlamy przykładowe dane w konsoli
foreach (var item in employee)
{
Console.WriteLine($"Id: {item.BusinessEntityID}, Płeć: {item.Gender}, Data zatrudnienia: {item.HireDate}");
}
Console.WriteLine();
NextAction();
}
static void HiredIn2007()
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
var employeeHiredIn2007 = db.Employee.Where(a => a.HireDate.Year == 2007);
// Wyświetlamy przykładowe dane w konsoli
foreach (var item in employeeHiredIn2007)
{
Console.WriteLine($"Id: {item.BusinessEntityID}, Płeć: {item.Gender}, Data zatrudnienia: {item.HireDate}");
}
Console.WriteLine();
NextAction();
}
static void SpecificEmployeeId(int id)
{
AdventureWorks2012_DataEntities db = new AdventureWorks2012_DataEntities();
Employee spec = db.Employee.Single(a => a.BusinessEntityID == id);
Console.WriteLine("Wskazany pracownik: ");
Console.WriteLine($"Id: {spec.BusinessEntityID}, Płeć: {spec.Gender}, Data zatrudnienia: {spec.HireDate}");
Console.WriteLine();
NextAction();
}
}
}
Adnotacja dotycząca relacji i Lazy Loading
Aby w pełni zrozumieć Entity Framework należy zrozumieć kilka
rzeczy takich jak: nazewnictwo, relacje między tabelami, relacje między encjami. Ponadto
koncepcja Lazy Loading pozwoli na dużo bardziej efektywne
używanie tej technologii.
Entity Framework jest używany juz od dłuższego czasu.
Wielu programistów zaczyna dopiero swoją przygodę z tą technologią i to dla nich został
napisany ten artykuł. Nie powinien być również traktowany jako kompletny poradnik. Ponadto,
kod jest napisany w bardzo prosty sposób i jest wiele miejsc, które należałoby zoptymalizować.
Ponieważ jednak chodziło o zrozumienie podstaw technologii dlatego kod został napisany w taki
a nie inny sposób.