Przyjrzyjmy się, jak wprowadzić Entity Framework Core 5 w projekcie ASP.NET Core w .NET 5. Zaczniemy od pustej bazy danych, w której mamy pełne pole do popisu i możemy dodawać tabele tak, jak chcemy.
W tym poście będziemy pracować z projektem PrimeHotel, który stworzyłem do celów edukacyjnych. Wszystko to jest dostępne w moim GitHub, więc możesz go pobrać bezpłatnie. Zobacz także ten post, aby dowiedzieć się jak uruchomić ten projekt: PrimeHotel – jak uruchomić projekt.
Czym jest Entity Framework Core
Entity Framework Core 5 to lekka, rozszerzalna, otwarta i wieloplatformowa wersja popularnej technologii dostępu do danych Entity Framework. EF Core jest obiektowym mapperem (O/RM), umożliwia programistom pracę z bazą danych przy użyciu obiektów .NET i eliminuje potrzebę korzystania z większości kodu dostępu do danych, który zwykle muszą pisać. Oznacza to koniec pisania komend w SQL. Sprawdza się doskonale w większości przypadków, jednak gdy trzeba pracować z dużą ilością danych, prawdopodobnie lepiej będzie napisać własne polecenia SQL.
Dodanie Entity Framework Core
Dodawanie EF Core 5 do projektu ASP.NET Core jest bardzo proste. Zacznij od instalacji pakietów NuGet:
- Microsoft.EntityFrameworkCore
- Microsoft.EntityFrameworkCore.Design
- Microsoft.EntityFrameworkCore.SqlServer
Po zakończeniu dodaj PrimeDbContext
do folderu Models, który wyglądałby następująco:
public class PrimeDbContext : DbContext { public PrimeDbContext(DbContextOptions<PrimeDbContext> options) : base(options) { } public virtual DbSet<Room> Rooms { get; set; } }
Teraz musimy dodać klasę Room
, która reprezentowałaby encję pokoju, z tabeli Rooms
.
public class Room { public int Id { get; set; } public int Number { get; set; } public string Description { get; set; } public DateTime LastBooked { get; set; } public int Level { get; set; } public RoomType RoomType { get; set; } public int NumberOfPlacesToSleep { get; set; } } public enum RoomType { Standard, Suite }
Teraz zajmiemy się częścią konfiguracyjną. Użyjmy pustej bazy danych, która jest hostowana przez nas lokalnie. Najłatwiejszym sposobem jest zainstalowanie SQL Server w wersji Express i skonfigurowanie jej lokalnie. Możesz jednak skonfigurować serwer bazy danych w kontenerze docker. Sprawdź mój post, jak to zrobić: Set up a SQL Server in a docker container.
W pliku appsettings.json
musimy wpisać nasz connection string. Powinien on wyglądać podobnie jak tutaj:
A teraz przejdźmy do pliku Startup.cs
, gdzie musimy skonfigurować EF Core, aby używał naszego connection stringa. W metodzie ConfigureServices
dodaj następujący wiersz:
// Entity Framework services.AddDbContext<PrimeDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("HotelDB")));
Zauważ, że użyłem nazwy HotelDB
, która jest nazwą mojego connection stringa z pliku appsettings.json
. To ważne, żeby te dwie wartości do siebie pasowały.
Wykonaliśmy już większość kluczowych rzeczy, ale potrzebujemy czegoś, co stworzyłoby tabele w bazie danych z naszego PrimeDbContext
. Do tej pory mamy tylko jedną, ale nadszedł właściwy czas na wprowadzenie mechanizmów migracji.
Dodanie migracji w EF Core
Migracje w EF Core bazy danch pozwalają wprowadzać zmiany w bazie danych tak, aby była ona aktualna z kodem aplikacji, które ją używa. Jest to bardzo istotny mechanizm, ponieważ zmiany w strukturze bazy danych wprowadzane są dość często, nawet przez wielu programistów, więc potrzebujemy uniwersalnego mechanizmu, żeby te zmiany śledzić i wprowadzać.
Dodawanie pierwszej migracji niewiele różni się od dodawania kolejnych. Musisz otworzyć okno terminala w lokalizacji projektu i wykonać polecenie:
dotnet ef migrations add InitMigration
Po pomyślnym wykonaniu tego polecenia zostanie wygenerowany plik migracji, w którym można sprawdzić, jakie zmiany zostaną zastosowane.
W tym pliku znajdziesz dwie metody: Up
i Down
. Reprezentują zmiany, kiedy migracja zostanie zastosowana i kiedy zostanie wycofana.
public partial class InitialCreate : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Rooms", columns: table => new { Id = table.Column<int>(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Number = table.Column<int>(nullable: false), Description = table.Column<string>(nullable: true), LastBooked = table.Column<DateTime>(nullable: false), Level = table.Column<int>(nullable: false), RoomType = table.Column<int>(nullable: false), NumberOfPlacesToSleep = table.Column<int>(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Rooms", x => x.Id); }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "Rooms"); } }
Ostatnią rzeczą do zrobienia jest uruchomienie aktualizacji bazy danych. Byłoby wspaniale, gdyby nasze zmiany były sprawdzane i stosowane przy każdym uruchomieniu projektu. Zobaczmy zatem, jak możemy to osiągnąć. Przede wszystkim przejdźmy do pliku Startup.cs
i utwórzmy metodę.
private void UpgradeDatabase(IApplicationBuilder app) { using (var serviceScope = app.ApplicationServices.CreateScope()) { var context = serviceScope.ServiceProvider.GetService<PrimeDbContext>(); if (context != null && context.Database != null) { context.Database.Migrate(); } } }
Ta metoda wykorzysta wbudowany mechanizm wstrzykiwania zależności do pobrania instancji naszego PrimeDbContext
i użycia jej do uruchomienia migracji bazy danych. Uruchomione zostaną tylko te, które nie zostały jeszcze zastosowane.
Pozostaje jeszcze jedna linia do dodania w metodzieConfigure
, na samym dole.
UpgradeDatabase(app);
Jeśli spojrzymy na bazę danych, zobaczymy, jakie migracje zostały zastosowane. Tabela __EFMigrationsHistory
zostanie utworzona automatycznie przez Entity Framework Core.
Używanie EF Core 5
Gdy mamy już wszystko na miejscu, odpowiednią konfigurację i migracje baz danych, możemy zacząć korzystać z mechanizmu O/RM.
Wszystkie operacje na tabelach w EF Core muszą przejść przez PrimeDbContext
. Korzystanie z niego jest bardzo proste, gdy zarejestrujemy go w klasie Startup.cs
, będzie on dostępny w dowolnej klasie do wstrzyknięcia i użycia. Spójrz na ten przykład prostych operacji CRUD w RoomController
.
[ApiController] [Route("[controller]")] public class RoomController : ControllerBase { private readonly PrimeDbContext primeDbContext; public RoomController(PrimeDbContext _primeDbContext) { primeDbContext = _primeDbContext; } [HttpGet] public async Task<IEnumerable<Room>> Get() { return await primeDbContext.Rooms.ToListAsync(); } [HttpGet("{id}")] public async Task<IActionResult> Get(int id) { var room = await primeDbContext.Rooms.FindAsync(id); if (room == null) { return NotFound(); } return Ok(room); } [HttpPost] public async Task<IActionResult> Post([FromBody] Room room) { var createdRoom = await primeDbContext.Rooms.AddAsync(room); await primeDbContext.SaveChangesAsync(); return Ok(createdRoom.Entity); } [HttpPut] public async Task<IActionResult> Put([FromBody] Room room) { var existingRoom = await primeDbContext.Rooms.FindAsync(room.Id); if (existingRoom == null) { return NotFound(); } existingRoom.Number = room.Number; existingRoom.Description = room.Description; existingRoom.LastBooked = room.LastBooked; existingRoom.Level = room.Level; existingRoom.RoomType = room.RoomType; existingRoom.NumberOfPlacesToSleep = room.NumberOfPlacesToSleep; var updatedRoom = primeDbContext.Update(existingRoom); await primeDbContext.SaveChangesAsync(); return Ok(updatedRoom.Entity); } [HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { var existingRoom = await primeDbContext.Rooms.FindAsync(id); if (existingRoom == null) { return NotFound(); } var removedRoom = primeDbContext.Rooms.Remove(existingRoom); await primeDbContext.SaveChangesAsync(); return Ok(removedRoom.Entity); } }
Zauważ, że w EF Core każda metoda ma wersję asynchroniczną. Wykorzystanie asynchroniczności jest dobrym pomysłem. W ten sposób Twój kod będzie szybszy i może być uruchamiany wydajniej z wieloma innymi żądaniami równolegle.
Ważne rzeczy, o których należy pamiętać:
- możemy wyszukiwać kolekcje encji w sposób, w jaki chcemy z LINQ, używając
Where
,Select
i innych metod, które na koniec wygenerują SQL ze wszystkich tych warunków - Jeśli tylko filtrujesz encje do wyświetlenia, możesz użyć
AsNoTracking()
w celu poprawy wydajności - Wywołanie bazy danych zostanie wykonane dopiero wtedy, gdy kod, który piszemy, wymaga wyników. Dzieje się tak na przykład, gdy używamy
ToListAsync
- Wszystkie zmiany, które wprowadzamy, należy zapisać za pomocą SaveChangesAsync, aby je zapisać w bazie danych
To tylko kilka punktów, o których należy pamiętać, ale jest jeszcze wiele rzeczy dziejących się pod spodem, o których warto wiedzieć. To wprowadzenie wystarczy jednak na początek i jest więcej niż wystarczające, aby samodzielnie zacząć pracę z Entity Framework Core.
Podsumowanie
Schludnie i wygodnie! Entity Framework Core jest idealny do prawie każdego prostego użycia bazy danych. Jestem pewien, że uznasz jego możliwości za przydatne i intuicyjne.
Cały kod opublikowany w tym poście jest dostępny na moim GitHub, więc możesz go dowolnie ściągać i modyfikować. Zerknij także na post jak uruchomić projekt PrimeHotel: PrimeHotel – jak uruchomić projekt
Dzięki za przeczytanie, daj mi znać, jeśli podoba Ci się ten post 🙂