Entity Framework Code First 솔리드 객체 변경 내용 추적

18490 단어 framework
Entity Framework Code First는 DbContext를 통과합니다.ChangeTracker는 실체 대상의 변동을 추적하고 추적을 실현하는 방식은 두 가지가 있는데 그것이 바로 변동 추적 스냅샷과 변동 추적 에이전트이다.
변동 추적 스냅샷: 앞의 몇 편의 에세이의 예는 모두 실체 대상의 변동 스냅샷 추적을 통해 데이터 조작을 실현하는 것이다. POCO 모델은 Entity Framework 실체 클래스 속성의 변동을 알리는 어떠한 논리도 포함하지 않는다.Entity Framework는 첫 번째 객체가 메모리에 로드될 때 스냅샷을 생성합니다. 스냅샷 추가는 질의를 반환하거나 DbSet에 객체를 추가할 때 발생합니다.Entity Framework에서 객체의 변경 사항을 알아야 할 경우 현재 엔티티와 스냅샷의 객체를 스캔하여 비교합니다.스캔 대비를 위한 방법은 DbContext를 호출하는 것입니다.ChangeTracker의 DetectChanges 방법입니다.
변동 추적 에이전트: 변동 추적 에이전트는 Entity Framework 실체 대상에게 변동이 발생했음을 주동적으로 알리는 메커니즘이다.예: 마운트 지연의 실현 방식.변동 추적 에이전트를 사용하려면 정의된 클래스 구조에서 Entity Framework는 실행할 때 POCO 클래스에서 동적 형식을 만들고 POCO 속성을 다시 쓸 수 있습니다.동적 에이전트는 다시 쓰기 속성과 Entity Framework 실체 대상의 변동을 알리는 논리를 포함하는 동적 유형입니다.
Entity Framework Code First에서 DbContext를 자동으로 호출할 수 있습니다.ChangeTracker.DetectChanges 방법:
  ◊ DbSet.Add
  ◊ DbSet.Find
  ◊ DbSet.Remove
  ◊ DbSet.Attach
  ◊ DbSet.Local
  ◊ DbContext.SaveChanges
  ◊ DbContext.GetValidationErrors
  ◊ DbContext.Entry
  ◊ DbChangeTracker.Entries
◊ DbSet에서 LINQ를 실행하는 모든 쿼리
1. DetectChanges 호출 시기를 제어합니다.
대부분의 실례 대상의 변동 조정은 Entity Framework에서 SaveChanges를 진행할 때 알 수 있지만 필요에 따라 변동 추적을 호출하여 현재 대상의 상태를 가져올 수도 있다.
Entity Framework Code First용 DbContext.DetectChanges는 인스턴스 객체의 변경 사항을 감지할 때 대부분 성능에 문제가 없습니다.그러나 대량의 실례 대상이 메모리에 있거나 DbContext가 대량의 조작을 할 때 자동적인 DetectChanges 행위는 어느 정도 성능에 영향을 줄 수 있다.Entity Framework는 필요할 때 수동으로 호출하는 자동 DetectChanges를 끄는 기능을 제공합니다.
using (var ctx = new PortalContext())
{
    ctx.Configuration.AutoDetectChangesEnabled = false;
}

예:
using (var ctx = new PortalContext())
{
    ctx.Configuration.AutoDetectChangesEnabled = false;

    var province = ctx.Provinces.Find(1);
    province.ProvinceName = "  ";

    Console.WriteLine("Before DetectChanges:{0}", ctx.Entry(province).State);

    ctx.ChangeTracker.DetectChanges();

    Console.WriteLine("After DetectChanges:{0}", ctx.Entry(province).State);
}

코드 실행 결과:
Before DetectChanges:Unchanged
After DetectChanges:Modified

2. 변동 추적이 없는 실체 조회 얻기
일부 상황에서, 우리는 읽기만 하는 데이터 기록을 조회하기만 하면 되고, 데이터 기록에 대해 어떠한 수정도 하지 않는다.이럴 때는 Entity Framework에서 불필요한 상태 변동 추적을 원하지 않으며, Entity Framework의 AsNoTracking 방법을 사용하여 변동 추적이 없는 검색 결과를 되돌려줄 수 있다.
using (var ctx = new PortalContext())
{
    foreach (var province in ctx.Provinces.AsNoTracking())
    {
        Console.WriteLine(province.ProvinceName);
    }
}

상기 코드에서 AsNotracking 방법을 사용하여 변동 추적이 없는 Province로 되돌아오는 DbSet을 조회하는데 변동 추적이 없기 때문에 되돌아오는 Province 집중 데이터에 대한 수정 사항은 SaveChanges()에 제출되지 않습니다.
AsNoTracking은 IQueryable에 정의된 확장 방법이기 때문에 LINQ 표현식 조회에도 사용할 수 있습니다.
using (var ctx = new PortalContext())
{
    var query = from p in ctx.Provinces.AsNoTracking()
                where p.ProvinceID > 10
                select p;
    foreach (var province in query)
    {
        Console.WriteLine(province.ProvinceName);
    }
}

또는
using (var ctx = new PortalContext())
{
    var query = from p in ctx.Provinces
                where p.ProvinceID > 10
                select p;
    query = query.AsNoTracking();

    foreach (var province in query)
    {
        Console.WriteLine(province.ProvinceName);
    }
}

참고: AsNoTracking을 사용하려면 참조 네임스페이스 using System을 추가해야 합니다.Data.Entity.
3. 단일 실체의 변동 추적 정보 및 조작
상태 속성 사용:
using (var ctx = new PortalContext())
{
    var province = ctx.Provinces.Find(10);
    DbEntityEntry<Province> entry = ctx.Entry(province);
    Console.WriteLine("Before Edit: {0}", entry.State);
    province.ProvinceName = "Test";
    ctx.ChangeTracker.DetectChanges();
    Console.WriteLine("After Edit: {0}", entry.State);
}

참고: DbEntityEntry는 네임스페이스 using System을 참조해야 합니다.Data.Entity.Infrastructure;
코드 실행 결과는 다음과 같습니다.
Before Edit: Unchanged
After Edit: Modified

4. 대상의 현재 값, 원시 값과 데이터베이스에 있는 값을 보기
DbEntityEntry를 사용하면 객체의 현재, 원본 및 데이터베이스에 있는 값을 얻을 수 있으며, DbPropertyValues는 객체의 특정 속성을 저장하는 데 사용됩니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

using Portal.Models;

namespace Portal
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new PortalContext())
            {
                var province = ctx.Provinces.Find(10);
                province.ProvinceName = "Test";

                ctx.Database.ExecuteSqlCommand("UPDATE Province SET ProvinceName = 'Testing' WHERE ProvinceID = 10");

                PrintChangeTrackingInfo(ctx, province);
            }
        }

        static void PrintChangeTrackingInfo(DbContext ctx, Province province)
        {
            var entry = ctx.Entry(province);
            Console.WriteLine(entry.State);

            Console.WriteLine("
Current Values:
"); PrintPropertyValues(entry.CurrentValues); Console.WriteLine("
Original Values:
"); PrintPropertyValues(entry.OriginalValues); Console.WriteLine("
Database Values:
"); PrintPropertyValues(entry.GetDatabaseValues()); } static void PrintPropertyValues(DbPropertyValues values) { foreach (var propertyName in values.PropertyNames) { Console.WriteLine("- {0}-{1}", propertyName, values[propertyName]); } } } }

코드 실행 결과:
Modified

Current Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-Test

Original Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-  

Database Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-Testing
       . . .

코드가 실행하는 SQL 문을 실행합니다.
exec sp_executesql N'SELECT 
[Limit1].[ProvinceID] AS [ProvinceID], 
[Limit1].[ProvinceNo] AS [ProvinceNo], 
[Limit1].[ProvinceName] AS [ProvinceName]
FROM ( SELECT TOP (2) 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[ProvinceNo] AS [ProvinceNo], 
    [Extent1].[ProvinceName] AS [ProvinceName]
    FROM [dbo].[Province] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @p0
)  AS [Limit1]',N'@p0 int',@p0=10
UPDATE Province SET ProvinceName = 'Testing' WHERE ProvinceID = 10
exec sp_executesql N'SELECT 
[Limit1].[ProvinceID] AS [ProvinceID], 
[Limit1].[ProvinceNo] AS [ProvinceNo], 
[Limit1].[ProvinceName] AS [ProvinceName]
FROM ( SELECT TOP (2) 
    [Extent1].[ProvinceID] AS [ProvinceID], 
    [Extent1].[ProvinceNo] AS [ProvinceNo], 
    [Extent1].[ProvinceName] AS [ProvinceName]
    FROM [dbo].[Province] AS [Extent1]
    WHERE [Extent1].[ProvinceID] = @p0
)  AS [Limit1]',N'@p0 int',@p0=10

코드가 실행되는 SQL 문에서 알 수 있듯이 마지막으로 데이터베이스에 있는 대상의 값을 얻었을 때 Entity Framework는 다시 데이터베이스에 가서 대상의 기록 값을 조회한다.
5. DbPropertyValues의 값 수정
DbPropertyValues의 값은 읽기 전용이 아니므로 처음 로드한 후에 수정할 수 있습니다.
using (var ctx = new PortalContext())
{
    ctx.Configuration.AutoDetectChangesEnabled = false;
    var province = ctx.Provinces.Find(10);
    ctx.Entry(province)
        .CurrentValues["ProvinceName"] = "  ";

    Console.WriteLine("Property Value:{0}", province.ProvinceName);
    Console.WriteLine("State:{0}", ctx.Entry(province).State);
}

실행 결과:
Property Value:  
State:Modified

위 코드에서는 자동 감지 변동이 비활성화되었음에도 불구하고 속성 값을 수정한 후에도 대상의 속성은Modified로 수정됩니다.엔티티 속성 수정은 Change Tracker API를 통해 수행되며, 엔티티 상태는 DetectChanges를 호출하지 않아도 Modified로 수정됩니다.
솔리드의 상태를 변경하지 않으려면 다음과 같이 수행됩니다.
using (var ctx = new PortalContext())
{
    ctx.Configuration.AutoDetectChangesEnabled = false;
    var province = ctx.Provinces.Find(10);

    var _province = ctx.Entry(province).CurrentValues.Clone();
    _province["ProvinceName"] = "  ";

    Console.WriteLine("Property Value:{0}", province.ProvinceName);
    Console.WriteLine("State:{0}", ctx.Entry(province).State);
}

실행 결과:
Property Value:Test
State:Unchanged

좋은 웹페이지 즐겨찾기