2022. 1. 5. 17:56ㆍEntityFramewok 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[] { }
));
}
'EntityFramewok Core (EF Core)' 카테고리의 다른 글
대용량 데이터 처리시 발생 하는 동시성 충돌 처리 (0) | 2024.03.18 |
---|---|
EF core 에서 column encryption 하기 (0) | 2023.09.25 |
EF Core - With Generic Host (Pagination) (0) | 2023.08.21 |