Wprowadzenie

W tym wpisie skupimy się na connected scenario - dane będą pobierane i modyfikowane w tym samym kontekście. Omówimy operację INSERT, UPDATE oraz DELETE.

Wykonanie każdej z powyższych operacji wiąże się ze zmianą stanu jednostki, tzw. EntityState. W scenariuszu który omawiamy instancja DbContext śledzi wszystkie encje dlatego wraz z każdą zmianą jest w stanie automatycznie ustawić odpowiedni EntityState. Spójrzcie na poniższy diagram w celu lepszej interpretacji stanów w zależności od przeprowadzanej operacji: EFCore: Entity State

Dodawanie danych

W poprzednim wpisie dodaliśmy dane wykorzystując dwie metody:

  • Add();
  • AddRange();

Szczegółowej analizy dokonamy na bazie części kodu z poprzedniego wpisu:

using (var context = new ApplicationDbContext())
{
	List<CarCompanies> carCompanies = new List<CarCompanies>()
	{
		new CarCompanies()
		{
			Brand = "Audi",
		},
		new CarCompanies()
		{
			Brand = "Maserati"
		}
	};

	// Dodajemy zbiór danych
	context.CarCompanies.AddRange(carCompanies);
	context.SaveChanges();
}
W pierwszym kroku tworzymy kolekcję danych przy wykorzystaniu zwykłej listy. Następnie dodajmy zbiór danych korzystając z poniższego kodu:
context.CarCompanies.AddRange(carCompanies);
Powyższe dane zostały dodane do naszego kontekstu a status EntityState został zmieniony na Added. Wraz z wykonaniem SaveChanges() dochodzi do utworzenia i wykonania instrukcji INSERT na określonej bazie danych.

Zanim przejdziemy dalej spróbujemy podejrzeć wykonane zapytanie SQL. EF Core 5.0 pozwala na łatwe podglądanie zapytań dla obiektów IQueryable - w tym jednak przypadku mamy doczynienia z poleceniami. Posłużymy się narzędziem SQL Server Profiler:

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [CarCompanies] ([Brand], [SaleId], [SalesId])
VALUES (@p0, @p1, @p2);
SELECT [CarCompanyId]
FROM [CarCompanies]
WHERE @@ROWCOUNT = 1 AND [CarCompanyId] = scope_identity();

',N'@p0 nvarchar(4000),@p1 int,@p2 int',@p0=N'Audi',@p1=0,@p2=NULL

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [CarCompanies] ([Brand], [SaleId], [SalesId])
VALUES (@p0, @p1, @p2);
SELECT [CarCompanyId]
FROM [CarCompanies]
WHERE @@ROWCOUNT = 1 AND [CarCompanyId] = scope_identity();

',N'@p0 nvarchar(4000),@p1 int,@p2 int',@p0=N'Maserati',@p1=0,@p2=NULL

Aktualizowanie danych

W obecnie omawianym scenariuszu EF Core śledzi wszystkie encje pobierane za pomocą kontekstu. Dzięki temu możemy w łatwy sposób dokonać aktualizacji danych. Dodatkowo framework dokonuje automatycznej modyfikacji EntityState na Modified - dzięki temu wywołanie metody SaveChanges() skutkuje wywołaniem polecenia UPDATE na określonej bazie danych. Spójrzcie na poniższy przykład:

using (var context = new ApplicationDbContext())
{
	// Pobranie określonego rekordu
	var company = context.CarCompanies.SingleOrDefault(a => a.Brand == "Audi");
				
	// Modyfikacja nazwy firmy
	company.Brand = "Pagani";

	// Zapisanie zmian -> wywołanie polecenia UPDATE
	context.SaveChanges();
}

Warto zaznaczyć, że wygenerowane polecenie UPDATE to tzw. partial-update - w poleceniu zawarte są tylko zmodyfikowane właściwości, pozostałe nie są dołączone do polecenia.

W pierwszym kroku pobraliśmy rekord zgodnie z przygotowanym warunkiem:

SELECT TOP(2) [c].[CarCompanyId], [c].[Brand], [c].[SaleId], [c].[SalesId]
FROM [CarCompanies] AS [c]
WHERE [c].[Brand] = N'Audi'
W kolejnym doszło do aktualizacji danych
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [CarCompanies] SET [Brand] = @p0
WHERE [CarCompanyId] = @p1;
SELECT @@ROWCOUNT;

',N'@p1 int,@p0 nvarchar(4000)',@p1=1,@p0=N'Pagani'

Kasowanie danych

Podobnie jak w poprzednim przypadku (wykonując operacje w obrębie kontekstu) w pierwszym kroku pobieramy interesujące nas dane. Następnie wykorzystując metodę Remove dochodzi do automatycznej zmiany EntityState na Deleted. W momencie wykonania metody SaveChanges() EF Core tworzy i wykonuje polecenie DELETE na określonej bazie danych. Spójrzcie poniżej:

using (var context = new ApplicationDbContext())
{
	// Pobranie określonego rekordu
	var company = context.CarCompanies.SingleOrDefault(a => a.Brand == "Pagani");

	// Zaznaczenie rekordu do usunięcia
	context.CarCompanies.Remove(company);

	// Zapisanie zmian -> wywołanie polecenia DELETE
	context.SaveChanges();
}

Tym razem wrzucam podgląd jedynie polecenia DELETE:

exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [CarCompanies]
WHERE [CarCompanyId] = @p0;
SELECT @@ROWCOUNT;

',N'@p0 int',@p0=1