プロパティ値の操作Working with property values

ほとんどの場合 Entity Framework は、エンティティインスタンスのプロパティの状態、元の値、および現在の値の追跡を行います。For the most part Entity Framework will take care of tracking the state, original values, and current values of the properties of your entity instances. ただし、接続が切断されたシナリオなど、EF がプロパティについての情報を表示または操作する場合もあります。However, there may be some cases - such as disconnected scenarios - where you want to view or manipulate the information EF has about the properties. このトピックで紹介するテクニックは、Code First および EF Designer で作成されたモデルに等しく使用できます。The techniques shown in this topic apply equally to models created with Code First and the EF Designer.

Entity Framework は、追跡対象のエンティティの各プロパティの2つの値を追跡します。Entity Framework keeps track of two values for each property of a tracked entity. 現在の値は、名前が示すように、エンティティ内のプロパティの現在の値です。The current value is, as the name indicates, the current value of the property in the entity. 元の値は、エンティティがデータベースから照会されたとき、またはコンテキストにアタッチされたときにプロパティに保持されていた値です。The original value is the value that the property had when the entity was queried from the database or attached to the context.

プロパティ値を操作するには、次の2つの一般的なメカニズムがあります。There are two general mechanisms for working with property values:

  • 1つのプロパティの値は、プロパティメソッドを使用して厳密に型指定された方法で取得できます。The value of a single property can be obtained in a strongly typed way using the Property method.
  • エンティティのすべてのプロパティの値は、DbPropertyValues オブジェクトに読み取ることができます。Values for all properties of an entity can be read into a DbPropertyValues object. DbPropertyValues は、プロパティ値を読み取って設定できるようにするために、ディクショナリに似たオブジェクトとして機能します。DbPropertyValues then acts as a dictionary-like object to allow property values to be read and set. DbPropertyValues オブジェクトの値は、別の DbPropertyValues オブジェクトの値、またはエンティティの別のコピーや単純なデータ転送オブジェクト (DTO) などの他のオブジェクトの値から設定できます。The values in a DbPropertyValues object can be set from values in another DbPropertyValues object or from values in some other object, such as another copy of the entity or a simple data transfer object (DTO).

以下のセクションでは、上記の両方のメカニズムを使用する例を示します。The sections below show examples of using both of the above mechanisms.

個々のプロパティの現在の値または元の値を取得および設定するGetting and setting the current or original value of an individual property

次の例では、プロパティの現在の値を読み取って、新しい値を設定する方法を示しています。The example below shows how the current value of a property can be read and then set to a new value:

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";
}

元の値の読み取りまたは設定を行うには、CurrentValue プロパティの代わりに OriginalValue プロパティを使用します。Use the OriginalValue property instead of the CurrentValue property to read or set the original value.

文字列を使用してプロパティ名を指定した場合、戻り値は "object" として入力されることに注意してください。Note that the returned value is typed as “object” when a string is used to specify the property name. 一方、ラムダ式が使用されている場合、戻り値は厳密に型指定されます。On the other hand, the returned value is strongly typed if a lambda expression is used.

このようにプロパティ値を設定した場合、新しい値が古い値と異なる場合にのみ、プロパティが変更済みとしてマークされます。Setting the property value like this will only mark the property as modified if the new value is different from the old value.

このようにプロパティ値が設定されている場合、自動検出の変更がオフになっていても、変更は自動的に検出されます。When a property value is set in this way the change is automatically detected even if AutoDetectChanges is turned off.

マップされていないプロパティの現在の値の取得と設定Getting and setting the current value of an unmapped property

データベースにマップされていないプロパティの現在の値も読み取ることができます。The current value of a property that is not mapped to the database can also be read. マップされていないプロパティの例としては、ブログの .Rsslink プロパティがあります。An example of an unmapped property could be an RssLink property on Blog. この値は、ブログ Id に基づいて計算される場合があるため、データベースに格納する必要はありません。This value may be calculated based on the BlogId, and therefore doesn't need to be stored in the database. 次に例を示します。For example:

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 を公開している場合は、現在の値を設定することもできます。The current value can also be set if the property exposes a setter.

マップされていないプロパティの値の読み取りは、マップされていないプロパティの Entity Framework 検証を実行する場合に便利です。Reading the values of unmapped properties is useful when performing Entity Framework validation of unmapped properties. 同じ理由から、現在の値は、コンテキストによって現在追跡されていないエンティティのプロパティに対して読み取りおよび設定できます。For the same reason current values can be read and set for properties of entities that are not currently being tracked by the context. 次に例を示します。For example:

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";
}

マップされていないプロパティや、コンテキストによって追跡されていないエンティティのプロパティには、元の値を使用できないことに注意してください。Note that original values are not available for unmapped properties or for properties of entities that are not being tracked by the context.

プロパティが変更済みとしてマークされているかどうかを確認するChecking whether a property is marked as modified

次の例は、個々のプロパティが変更済みとしてマークされているかどうかを確認する方法を示しています。The example below shows how to check whether or not an individual property is marked as modified:

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 が呼び出されたときにデータベースに更新として送信されます。The values of modified properties are sent as updates to the database when SaveChanges is called.

プロパティを変更済みとしてマークするMarking a property as modified

次の例は、個々のプロパティを強制的に変更済みとしてマークする方法を示しています。The example below shows how to force an individual property to be marked as modified:

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 が呼び出されたときに、そのプロパティのデータベースに更新が送信されます。Marking a property as modified forces an update to be send to the database for the property when SaveChanges is called even if the current value of the property is the same as its original value.

現在、個々のプロパティが変更済みとしてマークされた後で変更されないようにリセットすることはできません。It is not currently possible to reset an individual property to be not modified after it has been marked as modified. これは、将来のリリースでサポートする予定です。This is something we plan to support in a future release.

エンティティのすべてのプロパティの現在の値、元の値、およびデータベースの値の読み取りReading current, original, and database values for all properties of an entity

次の例では、エンティティのマップされたすべてのプロパティについて、現在の値、元の値、およびデータベース内の実際の値を読み取る方法を示しています。The example below shows how to read the current values, the original values, and the values actually in the database for all mapped properties of an 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]);
    }
}

現在の値は、エンティティのプロパティに現在格納されている値です。The current values are the values that the properties of the entity currently contain. 元の値は、エンティティが照会されたときにデータベースから読み取られた値です。The original values are the values that were read from the database when the entity was queried. データベースの値は、現在データベースに格納されている値です。The database values are the values as they are currently stored in the database. データベースの値を取得すると、データベースに対する同時編集が別のユーザーによって行われた場合など、エンティティがクエリされた後にデータベース内の値が変更された可能性があります。Getting the database values is useful when the values in the database may have changed since the entity was queried such as when a concurrent edit to the database has been made by another user.

別のオブジェクトからの現在または元の値の設定Setting current or original values from another object

追跡対象エンティティの現在の値または元の値は、別のオブジェクトから値をコピーすることによって更新できます。The current or original values of a tracked entity can be updated by copying values from another object. 次に例を示します。For example:

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; }
}

上記のコードを実行すると、次の出力が出力されます。Running the code above will print out:

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

この手法は、サービス呼び出しまたは n 層アプリケーションのクライアントから取得した値を使用してエンティティを更新するときに使用されることがあります。This technique is sometimes used when updating an entity with values obtained from a service call or a client in an n-tier application. 使用されるオブジェクトは、エンティティと同じ型である必要はありません。ただし、エンティティの名前と名前が一致するプロパティがある場合に限ります。Note that the object used does not have to be of the same type as the entity so long as it has properties whose names match those of the entity. 上記の例では、ブログ Dto のインスタンスを使用して、元の値を更新しています。In the example above, an instance of BlogDTO is used to update the original values.

他のオブジェクトからコピーしたときに異なる値に設定されているプロパティのみが、変更済みとしてマークされることに注意してください。Note that only properties that are set to different values when copied from the other object will be marked as modified.

ディクショナリからの現在または元の値の設定Setting current or original values from a dictionary

追跡対象エンティティの現在の値または元の値は、ディクショナリまたはその他のデータ構造から値をコピーすることによって更新できます。The current or original values of a tracked entity can be updated by copying values from a dictionary or some other data structure. 次に例を示します。For example:

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);
}

元の値を設定するには、CurrentValues プロパティの代わりに OriginalValues プロパティを使用します。Use the OriginalValues property instead of the CurrentValues property to set original values.

プロパティを使用してディクショナリから現在または元の値を設定するSetting current or original values from a dictionary using Property

前に示したように CurrentValues または OriginalValues を使用する代わりに、プロパティメソッドを使用して各プロパティの値を設定することもできます。An alternative to using CurrentValues or OriginalValues as shown above is to use the Property method to set the value of each property. これは、複合プロパティの値を設定する必要がある場合に適しています。This can be preferable when you need to set the values of complex properties. 次に例を示します。For example:

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];
    }
}

上記の例では、ドット形式の名前を使用して複雑なプロパティにアクセスしています。In the example above complex properties are accessed using dotted names. 複合プロパティにアクセスするその他の方法については、このトピックで後述する「複雑なプロパティについて」の2つのセクションを参照してください。For other ways to access complex properties see the two sections later in this topic specifically about complex properties.

現在、元、またはデータベースの値を含む複製されたオブジェクトの作成Creating a cloned object containing current, original, or database values

CurrentValues、OriginalValues、または GetDatabaseValues から返された DbPropertyValues オブジェクトを使用して、エンティティの複製を作成できます。The DbPropertyValues object returned from CurrentValues, OriginalValues, or GetDatabaseValues can be used to create a clone of the entity. この複製には、作成に使用された DbPropertyValues オブジェクトのプロパティ値が含まれます。This clone will contain the property values from the DbPropertyValues object used to create it. 次に例を示します。For example:

using (var context = new BloggingContext())
{
    var blog = context.Blogs.Find(1);

    var clonedBlog = context.Entry(blog).GetDatabaseValues().ToObject();
}

返されるオブジェクトはエンティティではなく、コンテキストによって追跡されていないことに注意してください。Note that the object returned is not the entity and is not being tracked by the context. 返されたオブジェクトには、他のオブジェクトに設定されたリレーションシップもありません。The returned object also does not have any relationships set to other objects.

複製されたオブジェクトは、データベースに対する同時更新に関連する問題を解決するのに役立ちます。特に、特定の型のオブジェクトへのデータバインドを含む UI が使用されている場合に役立ちます。The cloned object can be useful for resolving issues related to concurrent updates to the database, especially where a UI that involves data binding to objects of a certain type is being used.

複合プロパティの現在の値または元の値の取得と設定Getting and setting the current or original values of complex properties

複合オブジェクト全体の値は、プリミティブプロパティの場合と同様に、プロパティメソッドを使用して読み取って設定できます。The value of an entire complex object can be read and set using the Property method just as it can be for a primitive property. さらに、複合オブジェクトをドリルダウンして、そのオブジェクトのプロパティの読み取りや設定を行うことも、入れ子になったオブジェクトを使用することもできます。In addition you can drill down into the complex object and read or set properties of that object, or even a nested object. 次に例をいくつか示します。Here are some examples:

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;
}

元の値を取得または設定するには、CurrentValue プロパティの代わりに OriginalValue プロパティを使用します。Use the OriginalValue property instead of the CurrentValue property to get or set an original value.

複合プロパティにアクセスするには、プロパティまたは ComplexProperty メソッドのいずれかを使用することに注意してください。Note that either the Property or the ComplexProperty method can be used to access a complex property. ただし、追加のプロパティまたは ComplexProperty 呼び出しを使用して複合オブジェクトをドリルダウンする場合は、ComplexProperty メソッドを使用する必要があります。However, the ComplexProperty method must be used if you wish to drill down into the complex object with additional Property or ComplexProperty calls.

DbPropertyValues を使用した複合プロパティへのアクセスUsing DbPropertyValues to access complex properties

CurrentValues、OriginalValues、または GetDatabaseValues を使用してエンティティの現在、元、またはすべての値を取得すると、複合プロパティの値が入れ子になった DbPropertyValues オブジェクトとして返されます。When you use CurrentValues, OriginalValues, or GetDatabaseValues to get all the current, original, or database values for an entity, the values of any complex properties are returned as nested DbPropertyValues objects. これらの入れ子になったオブジェクトを使用して、複合オブジェクトの値を取得できます。These nested objects can then be used to get values of the complex object. たとえば、次のメソッドは、すべてのプロパティの値を出力します。これには、複合プロパティと入れ子になった複合プロパティの値が含まれます。For example, the following method will print out the values of all properties, including values of any complex properties and nested complex properties.

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]);
        }
    }
}

現在のすべてのプロパティ値を出力するには、次のようにメソッドを呼び出します。To print out all current property values the method would be called like this:

using (var context = new BloggingContext())
{
    var user = context.Users.Find("johndoe1987");

    WritePropertyValues("", context.Entry(user).CurrentValues);
}