내 문제는 EF Core가 기본적으로 클래스의 정의 된 모든 속성을로드하는 반면, 특별히 요청하지 않으면로드하지 않기를 원합니다.
예를 들어, 저자가있는 책의이 간단한 예를 보자 (이 예에서는 모든 모델이 동일하지만 사용 된 패턴을 보여주기 위해) :
데이터베이스 엔티티 :
using System;
namespace Test.Models.DBModels
{
public partial class Book
{
public int BookId { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public Author Author { get; set; }
}
}
DTO :
using System;
using System.Collections.Generic;
namespace Test.Models.DTOModels
{
public partial class BookDTO
{
public int BookId { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public AuthorDTO Author { get; set; }
}
}
뷰 모델 :
using System;
using System.Collections.Generic;
namespace Test.Models.ViewModels
{
public partial class BookVM
{
public int BookId { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public AuthorVM Author { get; set; }
}
}
EF가 자동으로 채우도록 지시 한 내용이므로이 클래스에 "가상"속성을 사용하지 않습니다.
DBContext :
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Test.Models.DBModels;
namespace Test.DAL
{
public partial class TestContext : DbContext
{
public TestContext()
{
}
public TestContext(DbContextOptions<TestContext> options)
: base(options)
{
}
public virtual DbSet<Author> Author { get; set; }
public virtual DbSet<Book> Book { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
//optionsBuilder.UseSqlServer("connectionstring");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
서비스:
using AutoMapper;
using System.Collections.Generic;
using System.Linq;
using Test.BLL.Interfaces;
using Test.DAL;
using Test.Models.DomainModels;
using Test.Models.DTOModels;
using Microsoft.EntityFrameworkCore;
using AutoMapper.QueryableExtensions;
using System.Linq.Expressions;
using System;
namespace Test.BLL.Implementations
{
public class BookService : IBookService
{
private readonly TestContext dbContext;
private readonly IMapper _mapper;
public BookService(TestContext dbContext, IMapper mapper)
{
this.dbContext = dbContext;
this._mapper = mapper;
}
public IQueryable<BookDTO> Get()
{
var books = dbContext.Book;
var dto = books.ProjectTo<BookDTO>(_mapper.ConfigurationProvider);
return dto;
}
public IQueryable<BookDTO> Get(params Expression<Func<Book, object>>[] includes)
{
var books = dbContext.Book
.Select(x => x);
foreach (var include in includes)
books = books.Include(include);
var dto = books.ProjectTo<BookDTO>(_mapper.ConfigurationProvider);
return dto;
}
}
}
제어 장치:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Test.BLL.Implementations;
using Test.Models.DTOModels;
using Test.Models.ViewModels;
namespace Test.WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
private readonly BookService BookService;
private readonly IMapper _mapper;
public BookController(BookService BookService, IMapper mapper)
{
this.BookService = BookService;
this._mapper = mapper;
}
public IActionResult Index()
{
var books = _mapper.Map<IEnumerable<BookDTO>, IEnumerable<BookVM>>(BookService.Get().ToList());
return Ok(books);
}
}
}
컨트롤러에서 BookService.Get (). ToList () 메소드를 호출하면 json 결과에서 Author가 자동으로 채워집니다.
{
"bookId":1,
"authorId":1,
"title":"Book A",
"author":{
"authorId":1,
"name":"Some Author"
}
}
반면 나는 그것을 원합니다.
{
"bookId":1,
"authorId":1,
"title":"Book A",
"author": null
}
Author 개체를 채우려는 것처럼 BookService.Get (x => x.Author) .ToList ()를 사용하여 오버로드 된 메서드를 호출합니다.
나는 이것이 열렬하거나 게으른 로딩 기능과 관련이 있다고 가정하지만 어떻게 해야할지 모르겠습니다. 편집 : 설명서 는 " 지연 로딩은 탐색 속성에 액세스 할 때 데이터베이스에서 관련 데이터가 투명하게로드됨을 의미합니다 "라고 설명합니다. 또한 " 열심히 로딩하는 것은 내가 원하는 동작이지만 내가 지정한 속성에 대해서만 초기 쿼리의 일부로 데이터베이스에서 관련 데이터가로드됨을 의미 합니다"라고 말합니다.
EF Core에는 속성을 포함하도록 특별히 요청한 경우에만 속성을 채울 수있는 방법이 있습니까?
Flater의 답변과 Lucian Bargaoanu의 의견으로 인해 올바른 구현 (명시 적 확장)이되었습니다. Automapper 매핑 프로파일에서 각 속성을 자동으로 확장하지 않기로 지정할 수 있습니다.
CreateMap<Book, BookDTO>()
.ForMember(x => x.Author, options => options.ExplicitExpansion())
.ReverseMap();
그런 다음 오버로드 된 Get 메소드를 변경하여 포함을 ProjectTo 메소드에 전달하십시오.
public IQueryable<BookDTO> Get(params Expression<Func<BookDTO, object>>[] includes)
{
var books = dbContext.Book
.Select(x => x);
var dto = books.ProjectTo<BookDTO>(_mapper.ConfigurationProvider, null, includes);
return dto;
}
따라서 기본적으로 BookService.Get (). ToList ()를 호출하면 다음과 같은 결과가 발생합니다.
{
"bookId":1,
"authorId":1,
"title":"Book A",
"author": null
}
그러나 BookService.Get (x => x.Author) .ToList ()를 호출하면 다음이 반환됩니다.
{
"bookId":1,
"authorId":1,
"title":"Book A",
"author":{
"authorId":1,
"name":"Some Author"
}
}
이것은 모든 속성이 EF Core에 의해 자동으로 채워지지 않고 AutoMapper를 계속 사용할 수 있음을 의미합니다.
이것은 EF 동작이 아니며 Automapper 동작입니다.
public IQueryable<BookDTO> Get()
{
var books = dbContext.Book;
var dto = books.ProjectTo<BookDTO>(_mapper.ConfigurationProvider);
return dto;
}
ProjectTo<>
의도적으로 매핑 할 수있는 모든 속성을 선택합니다. 당신이에 프로젝트에 말하면 BookDTO
, 그것은에 정의 된 모든 속성 채우기 위해 최선을 다할 것입니다 BookDTO
저자를 포함한다.
Entity Framework에는 일반적으로 게으르고 열성적인로드로 설명되는 탐색 속성로드와 관련하여 특정 동작이 있습니다. 처음에는 이것이 문제의 원인이라고 생각할 것입니다.
그러나 Select
를 사용하면 EF의 로딩 동작을 효과적으로 무시하고로드해야 할 내용을 명시 적으로 알려줍니다. 이것은 의도적으로 EF의 간단한 행동이 원하는 정확한 제어를 제공하지 않는 경우에 사용됩니다.
당신은 사용하지 않는 Select
,하지만 당신은 사용하는 ProjectTo<>
내부적으로 사용하는 Select
까지 EF에 관한 한, 당신은 "당신을 로딩 동작을 오버라이드 (override)되는 (가 Automapper 구성에 따라 생성) 수단 "(즉, Automapper)는 명시 적으로 EF에게 작성자를로드하도록 지시합니다.
올바른 속성을 사용하여 속성을 무시하도록 Automapper에 지시 할 수 있습니다.
public partial class Book
{
public int BookId { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
[NotMapped]
public Author Author { get; set; }
}
그러면 Automapper가 데이터베이스에서 관련 작성자를 가져 오지 않습니다.
그러나 ProjectTo<>
의 장점 중 하나는 더 이상로드하지 않거나 원하지 않는 작업을 관리 할 필요가 없으며 제공된 DTO를 기반으로 Automapper가이를 파악하도록하는 것입니다. DTO에 하나의 속성을 배치하는 것은 나쁘지 않지만 대규모로 적용하기 시작하면 개발 및 유지 관리의 복잡성이 증가합니다.
대신 저자 정보가 있고 다른 하나는없는 두 개의 DTO 클래스를 만드는 것이 좋습니다. 이렇게하면 매핑 동작을 수동으로 제어 할 필요가 없으며 (필요한 것 이상), 작성자없이이 DTO를 처리 할 때 수행 할 필요가없는 널 검사를 줄일 수 있습니다. 로드 중입니다.