Praca z wartościami właściwości
W większości przypadków program Entity Framework zajmie się śledzeniem stanu, oryginalnych wartości i bieżących wartości właściwości wystąpień jednostki. Mogą jednak istnieć pewne przypadki — takie jak scenariusze rozłączone — w których chcesz wyświetlić lub manipulować informacjami, jakie ma program EF na temat właściwości. Techniki przedstawione w tym temacie dotyczą modeli utworzonych przy użyciu podejścia „najpierw kod” i narzędzia EF Designer.
Program Entity Framework śledzi dwie wartości dla każdej właściwości śledzonej jednostki. Bieżąca wartość to, jak wskazuje nazwa, bieżąca wartość właściwości w jednostce. Oryginalna wartość to wartość, którą właściwość miała podczas wykonywania zapytania o jednostkę z bazy danych lub dołączonej do kontekstu.
Istnieją dwa ogólne mechanizmy pracy z wartościami właściwości:
- Wartość pojedynczej właściwości można uzyskać w sposób silnie typizowane przy użyciu metody Property.
- Wartości wszystkich właściwości jednostki można odczytać do obiektu DbPropertyValues. Następnie DbPropertyValues działa jako obiekt przypominający słownik, aby umożliwić odczytywanie i ustawianie wartości właściwości. Wartości w obiekcie DbPropertyValues można ustawić na podstawie wartości w innym obiekcie DbPropertyValues lub wartości w innym obiekcie, na przykład innej kopii jednostki lub prostego obiektu transferu danych (DTO).
W poniższych sekcjach przedstawiono przykłady użycia obu powyższych mechanizmów.
Pobieranie i ustawianie bieżącej lub oryginalnej wartości pojedynczej właściwości
W poniższym przykładzie pokazano, jak można odczytać bieżącą wartość właściwości, a następnie ustawić na nową wartość:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(3);
// Read the current value of the Name property
string currentName1 = context.Entry(blog).Property(u => u.Name).CurrentValue;
// Set the Name property to a new value
context.Entry(blog).Property(u => u.Name).CurrentValue = "My Fancy Blog";
// Read the current value of the Name property using a string for the property name
object currentName2 = context.Entry(blog).Property("Name").CurrentValue;
// Set the Name property to a new value using a string for the property name
context.Entry(blog).Property("Name").CurrentValue = "My Boring Blog";
}
Użyj właściwości OriginalValue zamiast właściwości CurrentValue, aby odczytać lub ustawić oryginalną wartość.
Zwróć uwagę, że zwracana wartość jest wpisywana jako "obiekt", gdy ciąg jest używany do określenia nazwy właściwości. Z drugiej strony zwracana wartość jest silnie typizowana, jeśli jest używane wyrażenie lambda.
Ustawienie takiej wartości właściwości spowoduje oznaczenie właściwości jako zmodyfikowanej tylko wtedy, gdy nowa wartość różni się od starej wartości.
Po ustawieniu wartości właściwości w ten sposób zmiana jest automatycznie wykrywana, nawet jeśli funkcja AutoDetectChanges jest wyłączona.
Pobieranie i ustawianie bieżącej wartości niezamapowanej właściwości
Bieżąca wartość właściwości, która nie jest mapowana na bazę danych, można również odczytać. Przykładem niezamapowanej właściwości może być właściwość RssLink w blogu. Ta wartość może być obliczana na podstawie identyfikatora BlogId i dlatego nie musi być przechowywana w bazie danych. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
// Read the current value of an unmapped property
var rssLink = context.Entry(blog).Property(p => p.RssLink).CurrentValue;
// Use a string to specify the property name
var rssLinkAgain = context.Entry(blog).Property("RssLink").CurrentValue;
}
Bieżącą wartość można również ustawić, jeśli właściwość uwidacznia element setter.
Odczytywanie wartości niezamapowanych właściwości jest przydatne podczas przeprowadzania walidacji niezamapowanych właściwości programu Entity Framework. Z tego samego powodu bieżące wartości można odczytywać i ustawiać dla właściwości jednostek, które nie są obecnie śledzone przez kontekst. Przykład:
using (var context = new BloggingContext())
{
// Create an entity that is not being tracked
var blog = new Blog { Name = "ADO.NET Blog" };
// Read and set the current value of Name as before
var currentName1 = context.Entry(blog).Property(u => u.Name).CurrentValue;
context.Entry(blog).Property(u => u.Name).CurrentValue = "My Fancy Blog";
var currentName2 = context.Entry(blog).Property("Name").CurrentValue;
context.Entry(blog).Property("Name").CurrentValue = "My Boring Blog";
}
Należy pamiętać, że oryginalne wartości nie są dostępne dla niezamapowanych właściwości lub właściwości jednostek, które nie są śledzone przez kontekst.
Sprawdzanie, czy właściwość jest oznaczona jako zmodyfikowana
W poniższym przykładzie pokazano, jak sprawdzić, czy pojedyncza właściwość jest oznaczona jako zmodyfikowana:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var nameIsModified1 = context.Entry(blog).Property(u => u.Name).IsModified;
// Use a string for the property name
var nameIsModified2 = context.Entry(blog).Property("Name").IsModified;
}
Wartości zmodyfikowanych właściwości są wysyłane jako aktualizacje bazy danych po wywołaniu funkcji SaveChanges.
Oznaczanie właściwości jako zmodyfikowanej
W poniższym przykładzie pokazano, jak wymusić oznaczenie pojedynczej właściwości jako zmodyfikowanej:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
context.Entry(blog).Property(u => u.Name).IsModified = true;
// Use a string for the property name
context.Entry(blog).Property("Name").IsModified = true;
}
Oznaczanie właściwości jako zmodyfikowanej wymusza wysłanie aktualizacji do bazy danych dla właściwości, gdy funkcja SaveChanges jest wywoływana, nawet jeśli bieżąca wartość właściwości jest taka sama jak jego oryginalna wartość.
Obecnie nie można zresetować pojedynczej właściwości, aby nie została zmodyfikowana po oznaczeniu jej jako zmodyfikowanej. Jest to coś, co planujemy obsługiwać w przyszłej wersji.
Odczytywanie bieżących, oryginalnych i wartości bazy danych dla wszystkich właściwości jednostki
W poniższym przykładzie pokazano, jak odczytywać bieżące wartości, oryginalne wartości i wartości rzeczywiście w bazie danych dla wszystkich zamapowanych właściwości jednostki.
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
// Make a modification to Name in the tracked entity
blog.Name = "My Cool Blog";
// Make a modification to Name in the database
context.Database.SqlCommand("update dbo.Blogs set Name = 'My Boring Blog' where Id = 1");
// Print out current, original, and database values
Console.WriteLine("Current values:");
PrintValues(context.Entry(blog).CurrentValues);
Console.WriteLine("\nOriginal values:");
PrintValues(context.Entry(blog).OriginalValues);
Console.WriteLine("\nDatabase values:");
PrintValues(context.Entry(blog).GetDatabaseValues());
}
public static void PrintValues(DbPropertyValues values)
{
foreach (var propertyName in values.PropertyNames)
{
Console.WriteLine("Property {0} has value {1}",
propertyName, values[propertyName]);
}
}
Bieżące wartości to wartości, które obecnie zawierają właściwości jednostki. Oryginalne wartości to wartości odczytane z bazy danych podczas wykonywania zapytania o jednostkę. Wartości bazy danych są wartościami, które są obecnie przechowywane w bazie danych. Pobieranie wartości bazy danych jest przydatne, gdy wartości w bazie danych mogły ulec zmianie od czasu wysłania zapytania do jednostki, na przykład gdy współbieżna edycja bazy danych została wykonana przez innego użytkownika.
Ustawianie bieżących lub oryginalnych wartości z innego obiektu
Bieżące lub oryginalne wartości śledzonej jednostki można zaktualizować, kopiując wartości z innego obiektu. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var coolBlog = new Blog { Id = 1, Name = "My Cool Blog" };
var boringBlog = new BlogDto { Id = 1, Name = "My Boring Blog" };
// Change the current and original values by copying the values from other objects
var entry = context.Entry(blog);
entry.CurrentValues.SetValues(coolBlog);
entry.OriginalValues.SetValues(boringBlog);
// Print out current and original values
Console.WriteLine("Current values:");
PrintValues(entry.CurrentValues);
Console.WriteLine("\nOriginal values:");
PrintValues(entry.OriginalValues);
}
public class BlogDto
{
public int Id { get; set; }
public string Name { get; set; }
}
Uruchomienie powyższego kodu spowoduje wyświetlenie:
Current values:
Property Id has value 1
Property Name has value My Cool Blog
Original values:
Property Id has value 1
Property Name has value My Boring Blog
Ta technika jest czasami używana podczas aktualizowania jednostki przy użyciu wartości uzyskanych z wywołania usługi lub klienta w aplikacji n-warstwowej. Należy pamiętać, że użyty obiekt nie musi być tego samego typu co jednostka, o ile ma właściwości, których nazwy pasują do tych z jednostki. W powyższym przykładzie wystąpienie obiektu BlogDTO służy do aktualizowania oryginalnych wartości.
Należy pamiętać, że tylko właściwości ustawione na różne wartości podczas kopiowania z innego obiektu zostaną oznaczone jako zmodyfikowane.
Ustawianie bieżących lub oryginalnych wartości ze słownika
Bieżące lub oryginalne wartości śledzonej jednostki można zaktualizować, kopiując wartości ze słownika lub innej struktury danych. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var newValues = new Dictionary<string, object>
{
{ "Name", "The New ADO.NET Blog" },
{ "Url", "blogs.msdn.com/adonet" },
};
var currentValues = context.Entry(blog).CurrentValues;
foreach (var propertyName in newValues.Keys)
{
currentValues[propertyName] = newValues[propertyName];
}
PrintValues(currentValues);
}
Użyj właściwości OriginalValues zamiast właściwości CurrentValues, aby ustawić oryginalne wartości.
Ustawianie bieżących lub oryginalnych wartości ze słownika przy użyciu właściwości
Alternatywą dla używania wartości CurrentValues lub OriginalValues, jak pokazano powyżej, jest użycie metody Property w celu ustawienia wartości każdej właściwości. Może to być preferowane, gdy trzeba ustawić wartości właściwości złożonych. Przykład:
using (var context = new BloggingContext())
{
var user = context.Users.Find("johndoe1987");
var newValues = new Dictionary<string, object>
{
{ "Name", "John Doe" },
{ "Location.City", "Redmond" },
{ "Location.State.Name", "Washington" },
{ "Location.State.Code", "WA" },
};
var entry = context.Entry(user);
foreach (var propertyName in newValues.Keys)
{
entry.Property(propertyName).CurrentValue = newValues[propertyName];
}
}
W powyższym przykładzie do właściwości złożonych uzyskuje się dostęp przy użyciu nazw kropkowanych. Inne sposoby uzyskiwania dostępu do złożonych właściwości można znaleźć w dwóch sekcjach w dalszej części tego tematu, w szczególności o złożonych właściwościach.
Tworzenie sklonowanego obiektu zawierającego bieżące, oryginalne lub wartości bazy danych
Obiekt DbPropertyValues zwracany z wartości CurrentValues, OriginalValues lub GetDatabaseValues może służyć do utworzenia klonu jednostki. Ten klon będzie zawierać wartości właściwości z obiektu DbPropertyValues użytego do jego utworzenia. Przykład:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();
}
Zwróć uwagę, że zwracany obiekt nie jest jednostką i nie jest śledzony przez kontekst. Zwrócony obiekt nie ma również żadnych relacji ustawionych na inne obiekty.
Sklonowany obiekt może być przydatny do rozwiązywania problemów związanych z współbieżnych aktualizacji bazy danych, zwłaszcza gdy używany jest interfejs użytkownika, który obejmuje powiązanie danych z obiektami określonego typu.
Pobieranie i ustawianie bieżących lub oryginalnych wartości właściwości złożonych
Wartość całego złożonego obiektu można odczytać i ustawić przy użyciu metody Property, tak jak może to być dla właściwości pierwotnej. Ponadto można przejść do szczegółów złożonego obiektu i odczytać lub ustawić właściwości tego obiektu, a nawet zagnieżdżonego obiektu. Oto kilka przykładów:
using (var context = new BloggingContext())
{
var user = context.Users.Find("johndoe1987");
// Get the Location complex object
var location = context.Entry(user)
.Property(u => u.Location)
.CurrentValue;
// Get the nested State complex object using chained calls
var state1 = context.Entry(user)
.ComplexProperty(u => u.Location)
.Property(l => l.State)
.CurrentValue;
// Get the nested State complex object using a single lambda expression
var state2 = context.Entry(user)
.Property(u => u.Location.State)
.CurrentValue;
// Get the nested State complex object using a dotted string
var state3 = context.Entry(user)
.Property("Location.State")
.CurrentValue;
// Get the value of the Name property on the nested State complex object using chained calls
var name1 = context.Entry(user)
.ComplexProperty(u => u.Location)
.ComplexProperty(l => l.State)
.Property(s => s.Name)
.CurrentValue;
// Get the value of the Name property on the nested State complex object using a single lambda expression
var name2 = context.Entry(user)
.Property(u => u.Location.State.Name)
.CurrentValue;
// Get the value of the Name property on the nested State complex object using a dotted string
var name3 = context.Entry(user)
.Property("Location.State.Name")
.CurrentValue;
}
Użyj właściwości OriginalValue zamiast właściwości CurrentValue, aby uzyskać lub ustawić oryginalną wartość.
Należy pamiętać, że właściwość lub metoda ComplexProperty może służyć do uzyskiwania dostępu do właściwości złożonej. Jednak należy użyć metody ComplexProperty, jeśli chcesz przejść do szczegółów obiektu złożonego z dodatkowymi wywołaniami Właściwości lub ComplexProperty.
Uzyskiwanie dostępu do złożonych właściwości przy użyciu właściwości DbPropertyValues
Jeśli używasz wartości CurrentValues, OriginalValues lub GetDatabaseValues, aby uzyskać wszystkie bieżące, oryginalne lub bazy danych wartości jednostki, wartości wszystkich złożonych właściwości są zwracane jako zagnieżdżone obiekty DbPropertyValues. Te zagnieżdżone obiekty mogą następnie służyć do pobierania wartości obiektu złożonego. Na przykład poniższa metoda wyświetli wartości wszystkich właściwości, w tym wartości wszystkich złożonych właściwości i zagnieżdżonych właściwości złożonych.
public static void WritePropertyValues(string parentPropertyName, DbPropertyValues propertyValues)
{
foreach (var propertyName in propertyValues.PropertyNames)
{
var nestedValues = propertyValues[propertyName] as DbPropertyValues;
if (nestedValues != null)
{
WritePropertyValues(parentPropertyName + propertyName + ".", nestedValues);
}
else
{
Console.WriteLine("Property {0}{1} has value {2}",
parentPropertyName, propertyName,
propertyValues[propertyName]);
}
}
}
Aby wyświetlić wszystkie bieżące wartości właściwości, metoda będzie wywoływana następująco:
using (var context = new BloggingContext())
{
var user = context.Users.Find("johndoe1987");
WritePropertyValues("", context.Entry(user).CurrentValues);
}