Práce s hodnotami vlastností
Ve většině případů se Entity Framework postará o sledování stavu, původních hodnot a aktuálních hodnot vlastností instancí entity. Může ale docházet k některým případům – například odpojeným scénářům – kdy chcete zobrazit nebo manipulovat s informacemi, které ef obsahuje o vlastnostech. Techniky uvedené v tomto tématu jsou rovnocenné pro modely vytvořené pomocí Code First a EF Designeru.
Entity Framework sleduje dvě hodnoty pro každou vlastnost sledované entity. Aktuální hodnota je, jak název označuje, aktuální hodnota vlastnosti v entitě. Původní hodnota je hodnota, kterou vlastnost měla, když byla entita dotazována z databáze nebo připojena k kontextu.
Existují dva obecné mechanismy pro práci s hodnotami vlastností:
- Hodnotu jedné vlastnosti lze získat silným typem pomocí Property metoda.
- Hodnoty pro všechny vlastnosti entity lze číst do DbPropertyValues objektu. DbPropertyValues pak funguje jako objekt podobný slovníku, který umožňuje čtení a nastavení hodnot vlastností. Hodnoty v DbPropertyValues objektu lze nastavit z hodnot v jiném DbPropertyValues objektu nebo z hodnot v jiném objektu, například z jiné kopie entity nebo jednoduchého objektu přenosu dat (DTO).
Následující části ukazují příklady použití obou výše uvedených mechanismů.
Získání a nastavení aktuální nebo původní hodnoty jednotlivé vlastnosti
Následující příklad ukazuje, jak lze aktuální hodnotu vlastnosti přečíst a pak nastavit na novou hodnotu:
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";
}
Ke čtení nebo nastavení původní hodnoty použijte vlastnost OriginalValue místo vlastnosti CurrentValue.
Všimněte si, že vrácená hodnota je zadána jako "object" při použití řetězce k zadání názvu vlastnosti. Na druhé straně je vrácená hodnota silného typu, pokud se použije výraz lambda.
Nastavení hodnoty vlastnosti, jako je tato, označí vlastnost jako upravenou, pokud se nová hodnota liší od staré hodnoty.
Pokud je hodnota vlastnosti nastavena tímto způsobem, změna se automaticky zjistí, i když je funkce AutoDetectChanges vypnutá.
Získání a nastavení aktuální hodnoty nemapované vlastnosti
Aktuální hodnotu vlastnosti, která není namapována na databázi, lze také přečíst. Příkladem nemapované vlastnosti může být vlastnost RssLink na blogu. Tato hodnota se může vypočítat na základě Id blogu, a proto nemusí být uložena v databázi. Příklad:
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;
}
Aktuální hodnotu lze také nastavit, pokud vlastnost zveřejňuje setter.
Čtení hodnot nemapovaných vlastností je užitečné při provádění ověřování nemapovaných vlastností entity Framework. Z stejného důvodu lze aktuální hodnoty číst a nastavit pro vlastnosti entit, které nejsou aktuálně sledovány kontextem. Příklad:
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";
}
Všimněte si, že původní hodnoty nejsou k dispozici pro nemapované vlastnosti ani pro vlastnosti entit, které nejsou sledovány kontextem.
Kontrola, jestli je vlastnost označená jako upravená
Následující příklad ukazuje, jak zkontrolovat, jestli je jednotlivá vlastnost označena jako upravená:
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;
}
Při zavolání SaveChanges se hodnoty upravených vlastností odesílají jako aktualizace databáze.
Označení vlastnosti jako změněné
Následující příklad ukazuje, jak vynutit označení jednotlivé vlastnosti jako změněné:
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;
}
Označení vlastnosti jako změněné vynutí odeslání aktualizace do databáze pro vlastnost při SaveChanges je volána, i když aktuální hodnota vlastnosti je stejná jako jeho původní hodnota.
V současné době není možné obnovit individuální vlastnost, která se po označení jako změněná. Toto je něco, co plánujeme podporovat v budoucí verzi.
Čtení aktuálních, původních a databázových hodnot pro všechny vlastnosti entity
Následující příklad ukazuje, jak číst aktuální hodnoty, původní hodnoty a hodnoty ve skutečnosti v databázi pro všechny mapované vlastnosti entity.
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]);
}
}
Aktuální hodnoty jsou hodnoty, které vlastnosti entity aktuálně obsahují. Původní hodnoty jsou hodnoty, které byly načteny z databáze při dotazech entity. Hodnoty databáze jsou hodnoty, které jsou aktuálně uloženy v databázi. Získání hodnot databáze je užitečné, když se hodnoty v databázi mohly od dotazování entity změnit, například když jiný uživatel provedl souběžnou úpravu databáze.
Nastavení aktuálních nebo původních hodnot z jiného objektu
Aktuální nebo původní hodnoty sledované entity lze aktualizovat zkopírováním hodnot z jiného objektu. Příklad:
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; }
}
Spuštění výše uvedeného kódu se vytiskne:
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
Tato technika se někdy používá při aktualizaci entity s hodnotami získanými z volání služby nebo klienta v n-vrstvé aplikaci. Všimněte si, že použitý objekt nemusí být stejného typu jako entita, pokud má vlastnosti, jejichž názvy odpovídají názvům entity. V předchozím příkladu se k aktualizaci původních hodnot používá instance BlogDTO.
Všimněte si, že při kopírování z jiného objektu budou označeny jako změněné pouze vlastnosti, které jsou nastaveny na různé hodnoty.
Nastavení aktuálních nebo původních hodnot ze slovníku
Aktuální nebo původní hodnoty sledované entity lze aktualizovat zkopírováním hodnot ze slovníku nebo jiné datové struktury. Příklad:
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);
}
K nastavení původních hodnot použijte OriginalValues vlastnost místo CurrentValues vlastnost.
Nastavení aktuálních nebo původních hodnot ze slovníku pomocí vlastnosti
Alternativou k použití CurrentValues nebo OriginalValues, jak je uvedeno výše, je použití Property metoda k nastavení hodnoty každé vlastnosti. To může být vhodnější, když potřebujete nastavit hodnoty složitých vlastností. Příklad:
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];
}
}
V příkladu výše jsou komplexní vlastnosti přístupné pomocí tečkovaných názvů. Další způsoby přístupu ke složitým vlastnostem najdete v dalších dvou částech tohoto tématu, konkrétně o složitých vlastnostech.
Vytvoření klonovaného objektu obsahujícího aktuální, původní nebo databázové hodnoty
Objekt DbPropertyValues vrácený z CurrentValues, OriginalValues nebo GetDatabaseValues lze použít k vytvoření klonu entity. Tento klon bude obsahovat hodnoty vlastnosti z DbPropertyValues objekt použitý k jeho vytvoření. Příklad:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();
}
Všimněte si, že vrácený objekt není entitou a není sledován kontextem. Vrácený objekt také nemá žádné relace nastavené na jiné objekty.
Klonovaný objekt může být užitečný pro řešení problémů souvisejících s souběžnými aktualizacemi databáze, zejména v případě, že se používá uživatelské rozhraní, které zahrnuje datové vazby k objektům určitého typu.
Získání a nastavení aktuálních nebo původních hodnot složitých vlastností
Hodnotu celého komplexního objektu lze číst a nastavit pomocí metody Property stejně jako to může být pro primitivní vlastnost. Kromě toho můžete přejít k podrobnostem o komplexním objektu a číst nebo nastavit vlastnosti tohoto objektu nebo dokonce vnořeného objektu. Zde je uvedeno několik příkladů:
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;
}
K získání nebo nastavení původní hodnoty použijte OriginalValue vlastnost namísto CurrentValue vlastnost.
Všimněte si, že vlastnost nebo ComplexProperty metoda lze použít pro přístup ke komplexní vlastnosti. Nicméně, ComplexProperty metoda musí být použita, pokud chcete přejít k podrobnostem do komplexního objektu s další vlastnosti nebo ComplexProperty volání.
Použití DbPropertyValues pro přístup ke složitým vlastnostem
Při použití CurrentValues, OriginalValues nebo GetDatabaseValues získat všechny aktuální, původní nebo databázové hodnoty entity, hodnoty všech komplexních vlastností budou vráceny jako vnořené DbPropertyValues objekty. Tyto vnořené objekty se pak dají použít k získání hodnot komplexního objektu. Například následující metoda vypíše hodnoty všech vlastností, včetně hodnot všech složitých vlastností a vnořených složitých vlastností.
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]);
}
}
}
Chcete-li vytisknout všechny aktuální hodnoty vlastností, metoda by byla volána takto:
using (var context = new BloggingContext())
{
var user = context.Users.Find("johndoe1987");
WritePropertyValues("", context.Entry(user).CurrentValues);
}