エンティティフレームワークに循環参照なしでドメインモデルを作成する

StackOverflow https://stackoverflow.com/questions/19840537

  •  29-07-2022
  •  | 
  •  

質問

以下に再現されている(DTOとAutomApperを使用)動作するソリューションを見つけましたが、リストする答えを好むでしょう 問題に対するさまざまなアプローチ 例があり、これは受け取った場合の答えとしてマークされます。

私のエンティティモデルには、子エンティティから親エンティティに移行するナビゲーションプロパティがあります。私のプロジェクトは泳いで働いていました。その後、ユニットテストに自動フィクストールを使用し始めましたが、テストに失敗し、循環参照があると言って自動フィクストールが失敗しました。

今、私はこのような循環参照ナビゲーションプロパティがエンティティフレームワーク内で問題ないことを認識していますが、この投稿を見つけました(Autofixtureで複雑な子供を作成するときに親プロパティの値を使用する)、Autofixtureの作成者であるMark Seemannが次のように述べています。

「記録のために、私は何年も循環参照を含むAPIを書いていないので、それらの親と子の関係を避けることは非常に可能です。」

したがって、子供と親の関係を避けるために、ドメインモデルをどのようにリファクタリングできるかを理解したいと思います。

以下は、問題のエンティティクラス、リポジトリメソッド、および私の見解で円形の参照を原因とするプロパティの使用方法です。 完璧な答えは、例で選択できるさまざまなオプションと、各アプローチの基本的な長所/短所を説明します。

注:Circular Referenceを引き起こすプロパティは、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のようなことを行うことができる間、円形の参照を削除するためにドメインモデルをリファクタリングするにはどうすればよいですか?

役に立ちましたか?

解決

持っていた 同様の問題 しばらく前にAutoFixtureとEntityFrameworkを使用しています。私の解決策は、自動フィクストールに拡張機能を追加することでした。これにより、いくつかの再帰でSUTを構築できます。その拡張は最近、自動フィクストールで採用されています。

しかし、あなたの質問は、自動フィクストールを再帰データ構造を構築する方法についてではなく、実際に可能ですが、再帰なしでドメインモデルを作成する方法については理解しています。

まず、ツリーまたはグラフ構造があります。ここでは、再帰以外は、結合ノードIDのゆるいことを介した間接を意味します。アソシエーションを定義する代わりに、ツリークエリをクエリまたはキャッシュするか、すべてをキャッシュし、ノードキールックアップによってトラバースする必要があります。ここでは、EFにあなたのために仕事をさせるのが非常に便利です。

他の共通構造は、ユーザー /ゲームのシナリオと同様の双方向ナビゲーション構造です。ここでは、ナビゲーションの流れを単一の方向に剪定するのはそれほど不便ではありません。ゲームからチームまでのOne Directionを省略すると、特定のゲームのすべてのチームを簡単に照会できます。したがって、ユーザーにはゲームのリストとチームのリストがあります。チームにはゲームのリストがあります。ゲームにはどちらにもナビゲーションの参照がありません。特定のゲームのすべてのユーザーを取得するには、次のようなものを書くことができます。

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

他のヒント

以下に再現されている(DTOとAutomApperを使用)動作するソリューションを見つけましたが、まだリストする回答を好むでしょう。 問題に対するさまざまなアプローチ 例として、特にこれが望ましいソリューションであるかどうか、またはナビゲーションプロパティに固執する必要があるかどうかは、自動フィックスを取り除き、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を使用して、1対多で多対多数の関係を指定する必要がありました。ここで解決策を見つけました: https://stackoverflow.com/a/16719203/1887885

これが役立つことを願っています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top