使用屬性值
在大部分的情況下,Entity Framework 會負責追蹤實體實例屬性的狀態、原始值和目前值。 不過,在某些情況下,您可能會想要檢視或操作 EF 關於屬性的資訊,例如已中斷連線的案例。 本主題所示範的技巧同樣適用於使用 Code First 和 EF 設計工具所建立的模型。
Entity Framework 會針對追蹤實體的每個屬性追蹤兩個值。 目前的值為實體中屬性的目前值,如名稱所示。 原始值是屬性從資料庫查詢實體或附加至內容時所擁有的值。
使用屬性值有兩個一般機制:
- 單一屬性的值可以使用 Property 方法,以強型別的方式取得。
- 實體的所有屬性值都可以讀入 DbPropertyValues 物件。 DbPropertyValues 接著會做為類似字典的物件,以允許讀取和設定屬性值。 DbPropertyValues 物件中的值可以從另一個 DbPropertyValues 物件中的值,或從其他物件中的值設定,例如實體的另一個複本或簡單的資料傳輸物件 (DTO)。
下列各節顯示使用上述兩種機制的範例。
取得和設定個別屬性的目前或原始值
下列範例顯示如何讀取屬性的目前值,然後設定為新的值:
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";
}
使用 OriginalValue 屬性,而不是 CurrentValue 屬性來讀取或設定原始值。
請注意,當字串用來指定屬性名稱時,傳回的值會輸入為 「object」。 另一方面,如果使用 Lambda 運算式,則傳回的值會強型別。
如果新的值與舊值不同,則設定這樣的屬性值只會將屬性標示為已修改。
以這種方式設定屬性值時,即使 AutoDetectChanges 已關閉,也會自動偵測變更。
取得和設定未對應的屬性目前值
也可以讀取未對應至資料庫之屬性的目前值。 未對應的屬性範例可能是 Blog 上的 RssLink 屬性。 此值可能會根據 BlogId 計算,因此不需要儲存在資料庫中。 例如:
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;
}
如果 屬性公開 setter,也可以設定目前的值。
執行未對應屬性的 Entity Framework 驗證時,讀取未對應的屬性值很有用。 基於相同的原因,目前值可以讀取和設定內容目前未追蹤之實體的屬性。 例如:
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";
}
請注意,原始值不適用於未對應的屬性,或內容未追蹤之實體的屬性。
檢查屬性是否標示為已修改
下列範例示範如何檢查是否將個別屬性標示為已修改:
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;
}
呼叫 SaveChanges 時,已修改屬性的值會以更新的形式傳送至資料庫。
將屬性標示為已修改
下列範例示範如何強制將個別屬性標示為已修改:
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;
}
當呼叫 SaveChanges 時,將屬性標示為修改時,強制將更新傳送至屬性的資料庫,即使屬性的目前值與其原始值相同也一樣。
目前無法重設個別屬性,使其在標示為修改之後無法修改。 這是我們計畫在未來版本中支援的內容。
讀取實體所有屬性的目前、原始和資料庫值
下列範例示範如何讀取目前值、原始值,以及實體所有對應屬性的實際資料庫中的值。
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]);
}
}
目前的值是實體目前包含之屬性的值。 原始值是查詢實體時從資料庫讀取的值。 資料庫值是值,因為它們目前儲存在資料庫中。 取得資料庫值時,當資料庫中的值可能已經變更,因為已查詢實體,例如其他使用者對資料庫的並行編輯時。
設定另一個物件的目前或原始值
從另一個物件複製值,即可更新追蹤實體的目前或原始值。 例如:
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; }
}
執行上述程式碼將會列印出來:
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
使用從服務呼叫或多層式應用程式中用戶端取得的值來更新實體時,有時會使用這項技術。 請注意,使用的物件不一定與實體的類型相同,只要其名稱符合實體的屬性。 在上述範例中,使用 BlogDTO 的實例來更新原始值。
請注意,只有從其他物件複製時設定為不同值的屬性才會標示為修改。
設定字典中的目前或原始值
從字典或其他資料結構複製值,即可更新追蹤實體的目前或原始值。 例如:
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);
}
使用 OriginalValues 屬性,而不是 CurrentValues 屬性來設定原始值。
使用 Property 從字典設定目前或原始值
使用 CurrentValues 或 OriginalValues 的替代方法是使用 Property 方法來設定每個屬性的值。 當您需要設定複雜屬性的值時,最好這樣做。 例如:
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];
}
}
在上述範例中,複雜屬性是使用點名稱來存取。 如需存取複雜屬性的其他方式,請參閱本主題稍後的兩節,特別是複雜屬性。
建立包含目前、原始或資料庫值的複製物件
從 CurrentValues、OriginalValues 或 GetDatabaseValues 傳回的 DbPropertyValues 物件可用來建立實體的複製品。 此複製品將包含用來建立它的 DbPropertyValues 物件的屬性值。 例如:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();
}
請注意,傳回的物件不是實體,而且不會由內容追蹤。 傳回的物件也沒有任何關聯性設定為其他物件。
複製的物件可用於解決與資料庫並行更新相關的問題,特別是使用牽涉到特定類型物件之資料系結的 UI。
取得和設定複雜屬性的目前或原始值
您可以使用 Property 方法讀取和設定整個複雜物件的值,就如同基本屬性一樣。 此外,您也可以向下切入至複雜物件,以及讀取或設定該物件的屬性,甚至是巢狀物件。 以下列出一些範例:
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;
}
使用 OriginalValue 屬性,而不是 CurrentValue 屬性來取得或設定原始值。
請注意,Property 或 ComplexProperty 方法可用來存取複雜屬性。 不過,如果您想要向下切入具有其他 Property 或 ComplexProperty 呼叫的複雜物件,則必須使用 ComplexProperty 方法。
使用 DbPropertyValues 存取複雜屬性
當您使用 CurrentValues、OriginalValues 或 GetDatabaseValues 來取得實體的所有目前、原始或資料庫值時,任何複雜屬性的值都會以巢狀 DbPropertyValues 物件的形式傳回。 這些巢狀物件接著可用來取得複雜物件的值。 例如,下列方法會列印出所有屬性的值,包括任何複雜屬性的值和巢狀複雜屬性的值。
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]);
}
}
}
若要列印出所有目前的屬性值,方法會呼叫如下:
using (var context = new BloggingContext())
{
var user = context.Users.Find("johndoe1987");
WritePropertyValues("", context.Entry(user).CurrentValues);
}