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 🙂