Wprowadzenie

W jednym z poprzednich wpisów (EF Core - UPDATE) w sekcji Zrozumieć EntityState pierwszy raz mieliśmy styczność z biblioteką Microsoft.EntityFrameworkCore.ChangeTracking. Pozwala ona na śledzenie stanu każdej encji w obrębie tego samego kontekstu. Dzięki niej sprawdziliśmy zmiany EntityState w zależności od defincji (lub braku) identyfikatora danej encji.

Klasa ChangeTracker rozpoczyna śledzenie wszystkich jednostek, które zostały pobrane przy użyciu DbContext (scenariusz połączony) i nie są modyfikowane poza tym kontekstem (scenariusz rozłączony). Entity Framework Core śledzi wszystkie zmiany jednostek i ich wartości – dzięki temu jest w stanie kompilować i wykonywać odpowiednie instrukcje DML (zbiór instrukcji pozwalających na przetwarzanie danych, tj. INSERT, UPDATE, DELETE oraz MERGE - kombinacja trzech wspomnianych instrukcji) na podpiętej bazie danych.

Stan encji reprezentowany jest przez typ wyliczeniowy Microsoft.EntityFrameworkCore.EntityState, który przyjmuje jedną z poniższych wartości:

  • Added;
  • Modified;
  • Deleted;
  • Unchanged;
  • Detached;

Wykorzystując kod z poprzedniego wpisu sprawdzimy automatyczne zmiany zachodzące na danej encji na podstawie różnych operacji:

private static void CheckEntityState(IEnumerable<EntityEntry> records)
{
	foreach (var row in records)
	{
		Console.WriteLine($"Encja: {row.Entity.GetType().Name}, EntityState: {row.State}");
	}
}

Stan: Unchanged

Stan od którego zaczynamy to: Unchanged. Dane pobrane za pomocą nieprzetworzonego zapytania SQL lub LINQ-to-Entities przyjmą stan 'niezmieniony':

// Wymagana paczka: Microsoft.EntityFrameworkCore.ChangeTracking
private static void CheckEntityState(IEnumerable<EntityEntry> records)
{
	foreach (var row in records)
	{
		Console.WriteLine($"Encja: {row.Entity.GetType().Name}, EntityState: {row.State}");
	}
}

static void Main(string[] args)
{
	using(var context = new ApplicationDbContext())
	{
		// pobranie pierwszej osoby z tabeli
		var person = context.Person.First();

		CheckEntityState(context.ChangeTracker.Entries());
	}
}

Stan: Added

Wszystkie nowe encje bez wartości klucza dodane w danym kontekście przy użyciu metody Add lub Update (ten przykład analizowaliśmy dwa wpisy temu) zostaną oznaczone stanem Added:

using(var context = new ApplicationDbContext())
{
	var person = new Person() { FullName = "Paweł" };
	context.Add(person); // Zmiana entity state na Added

	CheckEntityState(context.ChangeTracker.Entries());
}

Stan: Modified

Jeżeli wartość encji zostanie zmieniona w obrębie DbContext dojdzie do automatycznej zmiany stanu na Modified:

using(var context = new ApplicationDbContext())
{
	var person = context.Person.First();
	person.FullName = "Modified"; // Zmiana entity state na Modified

	CheckEntityState(context.ChangeTracker.Entries());
}

Stan: Deleted

Usunięcie jednostki z kontekstu przy użyciu metody Remove spowoduje automatyczną zmianę stanu na Deleted:

using(var context = new ApplicationDbContext())
{
	var person = context.Person.First();
	context.Person.Remove(person); // Zmiana entity state na Deleted

	CheckEntityState(context.ChangeTracker.Entries());
}

Stan: Detached

Jednostki utworzone lub pobrane poza bieżącym kontekstem będą oznaczone jako: Detached (odłączone) – temu zagadnieniu poświęciłem kilka osobnych wpisów omawiając ten scenariusz w zależności od różnych instrukcji. Z perspektywy tego przykładu najważniejsza jest informacja, że encje takie (zmiany) nie są śledzone przez istniejący DbContext:

// encja utworzona poza kontekstem
var person = new Person() { FullName = "Deteched" };

using(var context = new ApplicationDbContext())
{
	// Stan: detached
	Console.WriteLine(context.Entry(person).State);

	// Dołączenie jednostki i rozpoczęcie śledzenia
	context.Add<Person>(person);
	CheckEntityState(context.ChangeTracker.Entries());
}