EntityFramework Core Tip & Trick

2022. 1. 5. 17:56EntityFramewok Core (EF Core)

반응형

Performance 

1. AsNoTracking 을 넣어서 성능을 향상 시키자. (readonly query 일 경우)

개체 처리비용은 감소하지만 메모리 오버헤드는 있을수 있다.
 (actor 는 영화마다 각각의 Instance 를 갖는다.  )

var movies = database
    .Movies
    .AsNoTracking()
    .Include(m => m.Characters)
    .ThenInclude(c => c.Actor)
    .AsNoTracking()
    .OrderByDescending(x => x.WorldwideBoxOfficeGross);

 

2. AsNoTrackingWithIdentityResolution 을 사용하여 같은 객체를 여러번 과잉 할당 하는 것을 방지하자. 
(readonly query 일 경우)

(actor 가 같은 Instance 를 갖는다.  

동일한 Actor모델 의 모든 인스턴스는 동일한 객체를 가리키므로 과잉 할당을 방지한다.)

var movies = database
    .Movies
    .AsNoTrackingWithIdentityResolution()
    .Include(m => m.Characters)
    .ThenInclude(c => c.Actor)
    .AsNoTracking()
    .OrderByDescending(x => x.WorldwideBoxOfficeGross);

3. 지연로딩을 활성화 하지 마라

(알수 없는 너무 많은 문제를 야기한다.)
   
개발자 명령 프롬프트에서 아래 명령 실행 후 

dotnet add package Microsoft.EntityFrameworkCore.Proxies

    
코드상에서 아래와 같이 설정 하면

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
		=> optionsBuilder .UseLazyLoadingProxies() .UseSqlServer(myConnectionString);


아래 코드는 
모든 Movie( N )에 대해 모든 관련 Character엔터티( +1 )에 대해 데이터베이스를 별도로 호출합니다 
   

var movies = database
  .Movies
  .OrderByDescending(x => x.WorldwideBoxOfficeGross);

foreach (var movie in movies)
{
  	// a database call per Character
  foreach (var character in movie.Characters)
  {
  	// database call for Actor
  	Console.WriteLine(character.Actor.Name);
  }
}

4. Production 에서는 런타임에 코드상에서 Migrate 를 사용하지 말고 script 를 통해 DBA 가 기존 db 를 백업 후 적용하도록 유도하라.

dotnet ef migrations script [From] [To]

 

5. Query Filter 를 활용하라.

public abstract class Production
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime Release { get; set; }
    public bool IsDeleted { get; set; }

    public List<Character> Characters { get; set; } = new List<Character>();
    public List<Rating> Ratings { get; set; } = new List<Rating>();
}

이 OnModelCreating방법 에서는 Production엔터티에서 수행되는 모든 쿼리에 적용되는 쿼리 필터를 적용할 수 있다.

modelBuilder.Entity<Production>()
    .HasQueryFilter(m => !m.IsDeleted);

 

6. Constants in expressions

 

쿼리에 상수를 사용하면 실수로 EF Core의 내부 쿼리 캐시를 오염시키고 전체 쿼리 성능을 저하시킬 수 있다.


아래 구문은 동일해 보이지만 두 개의 개별 캐시 항목으로 컴파일 됨

var first = database.Movies.First(m => m.Id == 1);
var second = database.Movies.First(m => m.Id == 2);


아래와 같이 수정하면 하나로 됨

var id = 1;
var first = database.Movies.First(m => m.Id == id);
id = 2;
var second = database.Movies.First(m => m.Id == id);

 

7.  Prevent Sql Injection

 

아래와 같은 방식은 sql injection 에 대상이 될 수 있다.

var query = $"Select * From Movies where id = {id}";
......FromSqlRaw(query)

 

아래와 같이 변형 하여 예방하자

FromSqlInterpolated($"Select * From Movies where id = {id}")

8.  대규모 Update 는 EF 의 기본방식으로는 비효율 적이니 Database 의 raw query 를 이용해서 처리 하자.

 

아래 코드는 매우 비효율 적인 update 구문 이다. 

var movies = database
    .Movies
    .OrderByDescending(x => x.WorldwideBoxOfficeGross);

foreach (var movie in movies)
{
    movie.DurationInMinutes += 10; // trailers
}
database.SaveChanges();


대신 단일 SQL 쿼리를 사용하자

database.Database
    .ExecuteSqlRaw(
        "UPDATE [Productions] " +
        "SET [DurationInMinutes] = [DurationInMinutes] + 10 " +
        "WHERE Discriminator = 'Movie';");

9. Connection Resiliency ( 연결 복원력)

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(
            "<connection string>",
            options => options.EnableRetryOnFailure(
                maxRetryCount: 4,
                maxRetryDelay: TimeSpan.FromSeconds(1),
                errorNumbersToAdd: new int[] { }
            ));
}

 



참조 : https://blog.jetbrains.com/dotnet/2021/02/24/entity-framework-core-5-pitfalls-to-avoid-and-ideas-to-try/

반응형