Domanda

Ho trovato una soluzione che funziona (usando DTOS e Automapper), che è riprodotta di seguito, ma preferirei una risposta che elenca il Diversi approcci al problema con esempi e questo sarà contrassegnato come risposta se ricevuta.

Nel mio modello di entità ho una proprietà di navigazione che va da un'entità figlio all'entità genitore. Il mio progetto stava funzionando a nuoto. Poi ho iniziato a usare l'autofixture per i test unitari e il test è fallito, AutoFixture dicendo che avevo un riferimento circolare.

Ora, mi rendo conto che le proprietà di navigazione di riferimento circolare come questa sono OK all'interno di Entity Framework, ma ho trovato questo post (Usa il valore di una proprietà genitore quando si crea un figlio complesso nell'autofixture), dove Mark apparen, il creatore di AutoFixture afferma:

"Per la cronaca, non ho scritto un'API con un riferimento circolare per anni, quindi è del tutto possibile evitare quelle relazioni con i genitori/figli."

Quindi, voglio capire come un modello di dominio può essere rifatto per evitare le relazioni figlio/genitore.

Di seguito sono riportate le classi di entità in questione, il metodo del repository e il modo in cui uso la proprietà che causa il riferimento circolare a mio avviso. La risposta perfetta spiegherebbe le diverse opzioni che potrei scegliere con esempi e i pro/contro di base di ciascun approccio.

Nota: la proprietà che causa il riferimento circolare è l'utente, nel modello UserTeam.

Modelli:

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

Metodo del repository

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

Visualizzazione

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

Ora, se rimuovo la proprietà di navigazione "utente", non sarei in grado di fare '@userteam.user.username'

Quindi, come faccio a refactoring del modello di dominio per rimuovere il riferimento circolare, mentre è in grado di lanciare facilmente attraverso i giochi e fare qualcosa come usererteam.user.username?

È stato utile?

Soluzione

Ho avuto una Problema simile con AutoFixture e EntityFramework qualche tempo fa. La mia soluzione era quella di aggiungere un'estensione all'autofixture, che ti consente di creare un SUT con alcune ricorsioni. Quell'estensione è stata recentemente adottata nell'autofixture.

Ma capisco che la tua domanda non riguardava come realizzare strutture di dati ricorsive per costruzioni di autofixture, il che è davvero possibile, ma come creare modelli di dominio senza ricorsione.

Innanzitutto, hai strutture di alberi o grafici. Qui qualsiasi cosa tranne la ricorsione significherebbe indirezione attraverso ID nodi accoppiati sciolti. Invece di definire un'associazione, dovresti attraversare la query per query sugli alberi o memorizzare nella cache il tutto e attraversare la ricerca del nodo, che può essere poco pratico a seconda della dimensione dell'albero. Qui è molto conveniente farti fare il lavoro per te.

L'altra struttura comune è una struttura di navigazione a due vie simile allo scenario utente / gioco. Qui spesso non è così scomodo potare il flusso di navigazione in un'unica direzione. Se ometti One Direction, diciamo da un gioco a squadra, puoi comunque interrogare facilmente tutte le squadre per una determinata partita. Quindi: l'utente ha un elenco di giochi e un elenco di squadre. La squadra ha un elenco di giochi. I giochi non hanno alcun riferimento a navigazione a neanche a. Per ottenere tutti gli utenti per un gioco specifico potresti scrivere qualcosa di simile:

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

Altri suggerimenti

Ho trovato una soluzione che funziona (usando DTOS e Automapper), che è riprodotta di seguito, ma preferirei comunque una risposta che elenca il Diversi approcci al problema Con esempi, in particolare se si tratta di una soluzione desiderabile, o se dovrei attenersi alle proprietà di navigazione per quanto erano, sbarazzarmi dell'autofixture e quando si tratta di serializzare per JSON, utilizzare altri lavori intorno (attributi ecc.) ...

Quindi, nel mio modello di vista, ho aggiunto un paio di lezioni:

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

E nel mio controller, utilizzo Automapper per mappare gli oggetti Game / UserTeam dal repository ai miei oggetti DTO e restituire la vista 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);
}

Di recente ho avuto un problema simile che ha anche avuto un impatto sulla serializzazione di oggetti JSON. Ho deciso di rimuovere i riferimenti circolari dal mio modello di dati.

Ho rimosso per la prima volta le proprietà di navigazione ridondanti che stavano creando i riferimenti circolari. Mi sono assicurato che il mio albero di dati risultante avesse senso. Questo mi ha permesso di chiarire quali oggetti possiedono quali relazioni.

Ciò ha anche reso l'EF incapace di ragionare automaticamente sulle mie relazioni. Ho dovuto specificare le relazioni da uno a molti e da molti a molti usando il fluentapi. Ho trovato una soluzione qui: https://stackoverflow.com/a/16719203/1887885

Spero che questo sia utile.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top