Question

I have found a solution that works (using DTOs and AutoMapper), which is reproduced below, but I would prefer an answer that lists the different approaches to the problem with examples and this will be marked as the answer if received.

In my entity model I have a navigation property that goes from a child entity to the parent entity. My project was working swimmingly. Then I began to use AutoFixture for unit testing, and testing failed, AutoFixture saying I had a circular reference.

Now, I realise that circular reference navigation properties like this are OK within Entity Framework, but I found this post (Use value of a parent property when creating a complex child in AutoFixture), where Mark Seemann, the creator of AutoFixture states:

"For the record, I haven't written an API with a circular reference for years, so it's quite possible to avoid those Parent/Child relations."

So, I want to understand HOW a domain model can be refactored to avoid child/parent relations.

Below are the entity classes in question, the repository method, and how I use the property causing the circular reference in my View. The perfect answer would explain the different options I could choose from with examples, and the basic pros/cons of each approach.

Note: The property causing the circular reference is User, in the UserTeam model.

Models:

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; }
}

Repository Method

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;
        }
    }

View

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

Now, if I remove the 'User' navigation property, I wouldn't be able to do '@userteam.User.UserName'

So, how do I refactor the domain model to remove the circular reference, whilst being able to easily loop through Games, and do something like UserTeam.User.Username?

Was it helpful?

Solution

I had a similar problem with AutoFixture and EntityFramework a while ago. My solution was to add an extension to AutoFixture, that allows you to build a SUT with a few recursions. That extension has recently been adopted in AutoFixture.

But I understand that your question was not about how to make AutoFixture construct recursive data structures, which is indeed possible, but how to create domain models without recursion.

First, you have tree or graph structures. Here anything but recursion would mean indirection through loose coupled node ids. Instead of defining an association, you would have to traverse the tree query-by-query or cache the whole thing and traverse by node-key lookup, which may be impractical depending on the tree-size. Here it is very convenient to make EF do the work for you.

The other common structure is a two-way navigational structure similar to your user / game scenario. Here it is often not that inconvenient to prune the navigation flow to a single direction. If you omit one direction, say from game to team, you can still easily query all teams for a given game. So: User has a list of games and a list of teams. Team has a list of games. Games have no navigational reference to either. To get all users for a specific game you could write something like:

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

OTHER TIPS

I have found a solution that works (using DTOs and AutoMapper), which is reproduced below, but I would still prefer an answer that lists the different approaches to the problem with examples, in particular whether this is a desirable solution, or whether I should stick with the navigation properties as they were, get rid of AutoFixture, and when it comes to serializing for json just utilise other work arounds (attributes etc)...

So, in my View Model, I added a couple of classes:

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; }
}

And in my controller, I use AutoMapper to map the Game / UserTeam objects from the repository to my DTO objects, and return the IList _gamesDto to the View.

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);
}

I had a similar problem recently which also impacted serializing JSON objects. I decided to remove the circular references from my data model.

I first removed the redundant navigation properties which were creating the circular references. I made sure that my resulting tree of data made sense. This allowed me to make it clear which objects own which relationships.

This also made EF unable to automatically reason about my relationships. I had to specify the One-to-Many and Many-to-Many relationships using the FluentAPI. I found a solution here: https://stackoverflow.com/a/16719203/1887885

Hope this is helpful.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top