속성 값 작업

대부분의 경우 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";
}

원래 값을 읽거나 설정하려면 CurrentValue 속성 대신 OriginalValue 속성을 사용합니다.

문자열을 사용하여 속성 이름을 지정할 때 반환된 값은 "개체"로 입력됩니다. 반면에 반환 값은 람다 식을 사용하는 경우 강력한 형식이 됩니다.

다음과 같이 속성 값을 설정하면 새 값이 이전 값과 다른 경우에만 속성이 수정된 것으로 표시됩니다.

속성 값이 이러한 방식으로 설정되면 AutoDetectChanges가 꺼져 있더라도 변경 내용이 자동으로 검색됩니다.

매핑되지 않은 속성의 현재 값 가져오기 및 설정

데이터베이스에 매핑되지 않은 속성의 현재 값도 읽을 수 있습니다. 매핑되지 않은 속성의 예는 블로그의 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

이 기술은 서비스 호출 또는 n 계층 애플리케이션의 클라이언트에서 가져온 값으로 엔터티를 업데이트하는 경우에도 사용됩니다. 이름이 엔터티의 속성과 일치하는 경우 사용되는 개체가 엔터티와 동일한 형식일 필요는 없습니다. 위의 예제에서는 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);
}

원래 값을 설정하려면 CurrentValues 속성 대신 OriginalValues 속성을 사용합니다.

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

원래 값을 가져오거나 설정하려면 CurrentValue 속성 대신 OriginalValue 속성을 사용합니다.

Property 또는 ComplexProperty 메서드를 사용하여 복합 속성에 액세스할 수 있습니다. 그러나 ComplexProperty 메서드는 추가 Property 또는 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);
}