문제

아래에 재현된 작동하는 솔루션(DTO 및 AutoMapper 사용)을 찾았지만 다음을 나열하는 답변을 선호합니다. 문제에 대한 다양한 접근 방식 예가 포함되어 있으며 수신되면 답변으로 표시됩니다.

내 엔터티 모델에는 하위 엔터티에서 상위 엔터티로 이동하는 탐색 속성이 있습니다.내 프로젝트는 순조롭게 진행되고 있었습니다.그런 다음 단위 테스트에 AutoFixture를 사용하기 시작했고 테스트가 실패했습니다. AutoFixture는 순환 참조가 있다고 말했습니다.

이제 이와 같은 순환 참조 탐색 속성이 Entity Framework 내에서 괜찮다는 것을 알았지만 이 게시물을 찾았습니다(AutoFixture에서 복잡한 하위 항목을 생성할 때 상위 속성 값을 사용하세요.), AutoFixture의 창시자인 Mark Seemann은 다음과 같이 말합니다.

"참고로 저는 수년간 순환 참조가 포함된 API를 작성하지 않았으므로 이러한 상위/하위 관계를 피하는 것이 가능합니다."

따라서 저는 하위/부모 관계를 피하기 위해 도메인 모델을 리팩터링할 수 있는 방법을 이해하고 싶습니다.

다음은 문제의 엔터티 클래스, 저장소 메서드 및 내 뷰에서 순환 참조를 유발하는 속성을 사용하는 방법입니다. 완벽한 답변은 제가 선택할 수 있는 다양한 옵션과 각 접근 방식의 기본 장단점을 설명하는 것입니다.

메모:순환 참조를 발생시키는 속성은 UserTeam 모델의 User입니다.

모델:

public class UserProfile
{
    public UserProfile()
    {
        UserTeams = new HashSet<UserTeam>();
        Games = new HashSet<Game>();
    }

    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }       

    public virtual ICollection<UserTeam> UserTeams { get; set; }
    public virtual ICollection<Game> Games { get; set; }
}


public class Game
{
    public Game()
    {
        UserTeams = new HashSet<UserTeam>();
    }

    public int Id { get; set; }
    public int CreatorId { get; set; }

    public virtual ICollection<UserTeam> UserTeams { get; set; }
}


public class UserTeam
{
    public UserTeam()
    {
        UserTeam_Players = new HashSet<UserTeam_Player>();
    }

    public int Id { get; set; }
    public int UserId { get; set; }
    public int GameId { get; set; }

    public virtual UserProfile User { get; set; }
    public virtual ICollection<UserTeam_Player> UserTeam_Players { get; set; }
}

저장소 방법

public IEnumerable<Game> GetAllGames()
    {
        using (DataContext)
        {             
            var _games = DataContext.Games
                 .Include(x => x.UserTeams)
                 .Include(x => x.UserTeams.Select(y => y.User))
                 .ToList();
            if (_games == null)
            {
                // log error
                return null;
            }
            return _games;
        }
    }

보다

@model IEnumerable<Game>
@foreach (var item in Model){
    foreach (var userteam in item.UserTeams){
        <p>@userteam.User.UserName</p>
    }
}

이제 '사용자' 탐색 속성을 제거하면 '@userteam.User.UserName'을 수행할 수 없습니다.

그렇다면 게임을 쉽게 반복하고 userteam.user.username과 같은 일을 할 수있는 동안 원형 참조를 제거하기 위해 도메인 모델을 어떻게 리팩터링합니까?

도움이 되었습니까?

해결책

나는 가지고 있었다 비슷한 문제 얼마 전 AutoFixture 및 EntityFramework를 사용했습니다.내 솔루션은 AutoFixture에 확장을 추가하여 몇 번의 재귀를 통해 SUT를 구축하는 것이었습니다.해당 확장은 최근 AutoFixture에 채택되었습니다.

그러나 귀하의 질문은 AutoFixture가 실제로 가능한 재귀 데이터 구조를 구성하는 방법이 아니라 재귀 없이 도메인 모델을 만드는 방법에 관한 것이라는 것을 이해합니다.

첫째, 트리 또는 그래프 구조가 있습니다.여기서 재귀를 제외한 모든 것은 느슨하게 결합된 노드 ID를 통한 간접 참조를 의미합니다.연관을 정의하는 대신 쿼리별로 트리를 순회하거나 전체를 캐시하고 노드 키 조회별로 순회해야 하는데, 이는 트리 크기에 따라 실용적이지 않을 수 있습니다.여기에서는 EF가 귀하를 위해 작업을 수행하도록 하는 것이 매우 편리합니다.

다른 일반적인 구조는 사용자/게임 시나리오와 유사한 양방향 탐색 구조입니다.여기서는 탐색 흐름을 한 방향으로 잘라내는 것이 그다지 불편하지 않은 경우가 많습니다.예를 들어 게임에서 팀으로 한 방향을 생략하는 경우에도 해당 게임의 모든 팀을 쉽게 쿼리할 수 있습니다.그래서:사용자는 게임 목록과 팀 목록을 가지고 있습니다.팀에는 게임 목록이 있습니다.게임에는 탐색 참조가 없습니다.특정 게임에 대한 모든 사용자를 얻으려면 다음과 같이 작성할 수 있습니다.

var users = (from user in DataContext.Users
            from game in user.Games
            where game.Name == 'Chess'
            select user).Distinct()

다른 팁

아래에 재현 된 DTOS 및 AUTOMPAPPER를 사용하여 작동하는 솔루션을 찾았지만 여전히 다음을 나열하는 답을 선호합니다. 문제에 대한 다른 접근법 예제, 특히 이것이 바람직한 솔루션인지 또는 내비게이션 속성을 고수 해야하는지 여부, 자동 픽스처를 제거하고 JSON의 직렬화와 관련하여 다른 작업 (속성 등)을 활용할 때 ...

그래서 내보기 모델에서 몇 가지 수업을 추가했습니다.

public class GameDTO
{
    public int Id { get; set; }
    public int CreatorId { get; set; }

    public ICollection<UserTeamDTO> UserTeamsDTO { get; set; }
}

public class UserTeamDTO : UserTeam
{
    public UserProfile User { get; set; }
}

그리고 컨트롤러에서는 Automapper를 사용하여 저장소에서 DTO 객체에 게임 / Userteam 객체를 매핑하고 ILIST _GAMESDTO를보기로 반환합니다.

var _games = _gameRepository.GetAllGames();

IList<GameDTO> _gamesDto = new List<GameDTO>();
IList<UserTeamDTO> _userteamsDto = new List<UserTeamDTO>();
GameDTO _gameDto = new GameDTO();
UserTeamDTO _userteamDto = new UserTeamDTO();
Mapper.CreateMap<Game, GameDTO>();
Mapper.CreateMap<UserTeam, UserTeamDTO>();

foreach (Game _game in _games)
{
    foreach (UserTeam _userteam in _game.UserTeams)
    {
        _userteamDto = Mapper.Map<UserTeamDTO>(_userteam);
        _userteamDto.User = _userRepository.GetUser(_userteam.UserId);
        _userteamsDto.Add(_userteamDto);
    }

    _gameDto = Mapper.Map<GameDTO>(_game);
    _gameDto.UserTeamsDTO = _userteamsDto;
    _gamesDto.Add(_gameDto);
}

최근에 비슷한 문제가있어 JSON 객체 직렬화에도 영향을 미쳤습니다. 내 데이터 모델에서 원형 참조를 제거하기로 결정했습니다.

먼저 원형 참조를 생성하는 중복 내비게이션 속성을 제거했습니다. 결과적인 데이터 트리가 의미가 있는지 확인했습니다. 이를 통해 어떤 객체가 어떤 관계를 소유하고 있는지 명확하게 할 수있었습니다.

이것은 또한 EF가 내 관계에 대해 자동으로 추론 할 수 없게 만들었습니다. Fluentapi를 사용하여 일대일 및 다수의 관계를 지정해야했습니다. 여기에서 해결책을 찾았습니다. https://stackoverflow.com/a/16719203/1887885

이것이 도움이되기를 바랍니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top