Подтвердить что ты не робот

Создание модели домена без круговых ссылок в Entity Framework

Я нашел решение, которое работает (с использованием DTO и AutoMapper), которое воспроизводится ниже, но я бы предпочел ответ, который перечисляет различные подходы к проблеме с примерами, и это будет отмечено как ответ, если он получен.

В моей модели сущности у меня есть свойство навигации, которое переходит от дочернего объекта к родительскому объекту. Мой проект работал плавно. Затем я начал использовать AutoFixture для модульного тестирования, а тестирование завершилось неудачно, AutoFixture заявила, что у меня есть круговая ссылка.

Теперь я понимаю, что такие свойства кольцевой эталонной навигации, как это, находятся в порядке внутри Entity Framework, но я нашел это сообщение (Использовать значение родительского свойства при создании сложного дочернего элемента в AutoFixture), где Марк Семанн, создатель AutoFixture, утверждает:

"Для записи я не писал API с круглой ссылкой в ​​течение многих лет, поэтому вполне можно избежать этих отношений родителя/ребенка".

Итак, я хочу понять, КАК модель домена может быть реорганизована, чтобы избежать отношений с дочерними/родительскими.

Ниже приведены классы сущностей, метод репозитория, и как я использую свойство, вызывающее циклическую ссылку в моем представлении. Идеальный ответ объяснял бы различные варианты, которые я мог бы выбрать с примерами, и основные плюсы/минусы каждого подхода.

Примечание. Свойство, вызывающее циклическую ссылку, является User, в модели UserTeam.

Модели:

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?

4b9b3361

Ответ 1

У меня была похожая проблема с AutoFixture и EntityFramework некоторое время назад. Мое решение состояло в том, чтобы добавить расширение к AutoFixture, которое позволяет вам построить SUT с несколькими рекурсиями. Это расширение было недавно принято в AutoFixture.

Но я понимаю, что ваш вопрос заключался не в том, как сделать AutoFixture конструкцией рекурсивных структур данных, что действительно возможно, но как создать модели домена без рекурсии.

Сначала у вас есть структуры дерева или графа. Здесь ничего, кроме рекурсии, означало бы косвенную передачу через свободные фишки node. Вместо определения ассоциации вам придется пересекать дерево по запросу или кэшировать все это и проходить через node -key lookup, что может быть нецелесообразным в зависимости от размера дерева. Здесь очень удобно сделать EF для вас.

Другая общая структура - это двухсторонняя навигационная структура, аналогичная вашему сценарию пользователя/игры. Здесь часто не так неудобно обрезать поток навигации в одном направлении. Если вы опустите одно направление, скажем, из игры в команду, вы все равно можете запросить все команды для данной игры. Итак: у пользователя есть список игр и список команд. У команды есть список игр. В играх нет навигационной ссылки. Чтобы получить всех пользователей для конкретной игры, вы можете написать что-то вроде:

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

Ответ 2

Я нашел решение, которое работает (с использованием DTO и AutoMapper), которое воспроизводится ниже, но я бы предпочел ответ, в котором перечислены различные подходы к проблеме с примерами, в частности, это желательное решение, или я должен придерживаться свойств навигации, как они были, избавиться от AutoFixture, и когда дело доходит до сериализации для 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 для сопоставления объектов Game/UserTeam из репозитория с моими объектами DTO и возвращает 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);
}

Ответ 3

Недавно у меня была аналогичная проблема, которая также повлияла на сериализацию объектов JSON. Я решил удалить круговые ссылки из моей модели данных.

Сначала я удалил избыточные навигационные свойства, которые создавали циклические ссылки. Я убедился, что полученное дерево данных имеет смысл. Это позволило мне понять, какие объекты принадлежат этим отношениям.

Это также заставило EF неспособно автоматически рассуждать о моих отношениях. Мне пришлось указать отношения "один ко многим" и "многие ко многим", используя FluentAPI. Я нашел решение здесь: fooobar.com/info/410804/...

Надеюсь, что это будет полезно.