수색…


AsNoTracking 사용

나쁜 예 :

var location =  dbContext.Location
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

위 코드는 단순히 엔티티를 수정하거나 추가하지 않고 반환하기 만하므로 추적 비용을 피할 수 있습니다.

좋은 예 :

var location =  dbContext.Location.AsNoTracking()
                     .Where(l => l.Location.ID == location_ID)
                     .SingleOrDefault();

return location;

AsNoTracking() 함수를 사용할 때 엔티티가 컨텍스트에 의해 추적되지 않는다는 것을 명시 적으로 Entity Framework에 알립니다. 이는 데이터 저장소에서 많은 양의 데이터를 검색 할 때 특히 유용 할 수 있습니다. 그러나 추적되지 않은 항목을 변경하려면 SaveChanges 를 호출하기 전에 첨부해야합니다.

필수 데이터로드 중

코드에서 자주 볼 수있는 한 가지 문제는 모든 데이터를로드하는 것입니다. 이렇게하면 서버의로드가 크게 증가합니다.

모델에 10 개의 필드가있는 "위치"라는 모델이 있지만 모든 필드가 동시에 필요하지는 않습니다. 해당 모델의 'LocationName'매개 변수 만 원한다고 가정 해 봅시다.

나쁜 예

var location =  dbContext.Location.AsNoTracking()
                         .Where(l => l.Location.ID == location_ID)
                         .SingleOrDefault();

return location.Name;

좋은 예

var location =  dbContext.Location
                          .Where(l => l.Location.ID == location_ID)
                          .Select(l => l.LocationName);
                          .SingleOrDefault();

return location;

"좋은 예"의 코드는 'LocationName'만 가져오고 다른 것은 가져 오지 않습니다.

이 예에서는 엔티티가 구체화되지 않으므로 AsNoTracking() 은 필요하지 않습니다. 아무래도 추적 할 수있는 것이 없습니다.

익명 형식으로 더 많은 필드를 가져 오는 중

var location = dbContext.Location
                    .Where(l => l.Location.ID == location_ID)
                    .Select(l => new { Name = l.LocationName, Area = l.LocationArea })
                    .SingleOrDefault();

return location.Name + " has an area of " + location.Area;

앞의 예제와 마찬가지로 'LocationName'과 'LocationArea'필드 만 데이터베이스에서 검색되며 익명 유형은 원하는만큼의 값을 가질 수 있습니다.

가능하면 메모리가 아니라 데이터베이스에서 쿼리를 실행하십시오.

텍사스에 얼마나 많은 카운티가 있는지 세고 싶다고합시다.

var counties = dbContext.States.Single(s => s.Code == "tx").Counties.Count();

쿼리는 정확하지만 비효율적입니다. States.Single(…) 은 데이터베이스에서 상태를로드합니다. 다음 Counties 는 두 번째 쿼리에서 모든 필드가있는 254 개 카운티를 모두로드합니다. .Count() 는로드 된 Counties 컬렉션의 메모리 에서 수행 됩니다 .
우리는 필요하지 않은 많은 데이터를로드했으며 더 잘할 수 있습니다.

var counties = dbContext.Counties.Count(c => c.State.Code == "tx");

여기에서는 하나의 쿼리 만 수행합니다.이 쿼리는 SQL에서 개수와 조인으로 변환됩니다. 데이터베이스에서 카운트 만 반환합니다. 반환되는 행, 필드 및 객체 생성을 저장했습니다.

IQueryable<T>IEnumerable<T> 의 컬렉션 유형을 살펴보면 쿼리가 어디에 만들어 졌는지 쉽게 알 수 있습니다.

여러 쿼리를 비동기 및 병렬로 실행

비동기 쿼리를 사용할 때 동시에 여러 쿼리를 실행할 수 있지만 같은 컨텍스트에서는 실행할 수 없습니다. 한 쿼리의 실행 시간이 10 초인 경우 나쁜 예제의 시간은 20 초가되고 좋은 예제의 시간은 10 초가됩니다.

나쁜 예

IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

using(var context = new Context())
{
    result1 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    result2 = await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
}

좋은 예

public async Task<IEnumerable<TResult>> GetResult<TResult>()
{
    using(var context = new Context())
    {
        return await context.Set<TResult1>().ToListAsync().ConfigureAwait(false);
    }
}



IEnumerable<TResult1> result1;
IEnumerable<TResult2> result2;

var result1Task = GetResult<TResult1>();
var result2Task = GetResult<TResult2>();

await Task.WhenAll(result1Task, result2Task).ConfigureAwait(false);

var result1 = result1Task.Result;
var result2 = result2Task.Result;

변경 내용 추적 및 프록시 생성 비활성화

데이터 만 가져오고 수정하지 않으려는 경우 변경 내용 추적 및 프록시 생성을 해제 할 수 있습니다. 이렇게하면 성능이 향상되고 지연로드가 방지됩니다.

나쁜 예 :

using(var context = new Context())
{
    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

좋은 예 :

using(var context = new Context())
{
    context.Configuration.AutoDetectChangesEnabled = false;
    context.Configuration.ProxyCreationEnabled = false;

    return await context.Set<MyEntity>().ToListAsync().ConfigureAwait(false);
}

특히 컨텍스트의 생성자 내에서이 속성을 해제하는 것이 일반적입니다. 특히 솔루션 전체에서 이러한 속성을 설정하려는 경우에는 특히 그렇습니다.

public class MyContext : DbContext
{
    public MyContext()
        : base("MyContext")
    {
        Configuration.AutoDetectChangesEnabled = false;
        Configuration.ProxyCreationEnabled = false;
    }

    //snip
}

스텁 엔터티 작업

ProductCategory 가 다 대다 관계에 있다고 가정 해보십시오.

public class Product
{
    public Product()
    {
        Categories = new HashSet<Category>();
    }
    public int ProductId { get; set; }
    public string ProductName { get; set; }
    public virtual ICollection<Category> Categories { get; private set; }
}

public class Category
{
    public Category()
    {
        Products = new HashSet<Product>();
    }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public virtual ICollection<Product> Products { get; set; }
}

우리가 추가하려면 Category A와 Product , 우리는 제품을로드하고 그에 카테고리를 추가 할 Categories 예를 들어, :

나쁜 예 :

var product = db.Products.Find(1);
var category = db.Categories.Find(2);
product.Categories.Add(category);
db.SaveChanges();

( dbDbContext 서브 클래스).

그러면 ProductCategory 사이의 접합 테이블에 하나의 레코드가 작성됩니다. 그러나이 테이블에는 두 개의 Id 값만 들어 있습니다. 하나의 작은 레코드를 만들기 위해 두 개의 엔티티를 모두로드하는 것은 리소스 낭비입니다.

보다 효율적인 방법은 최소한의 데이터 만 포함하는 스텁 엔티티 , 즉 메모리에 생성 된 엔티티 객체를 사용하는 것입니다.이 엔티티 객체는 대개 Id 값만 포함합니다. 이것은 다음과 같습니다.

좋은 예 :

// Create two stub entities
var product = new Product { ProductId = 1 };
var category = new Category { CategoryId = 2 };

// Attach the stub entities to the context
db.Entry(product).State = System.Data.Entity.EntityState.Unchanged;
db.Entry(category).State = System.Data.Entity.EntityState.Unchanged;

product.Categories.Add(category);
db.SaveChanges();

최종 결과는 동일하지만 데이터베이스에 대한 두 번의 라운드 트립을 피할 수 있습니다.

중복 방지

연결이 이미 존재하는지 여부를 확인하려는 경우 싼 쿼리로 충분합니다. 예 :

var exists = db.Categories.Any(c => c.Id == 1 && c.Products.Any(p => p.Id == 14));

다시 말하지만 전체 엔티티를 메모리로로드하지는 않습니다. 사실상 접합부 테이블을 조회하고 부울 만 리턴합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow