在Entity Framework中创建没有循环引用的域模型

jag*_*jag 15 entity-framework autofixture

我找到了一个有效的解决方案(使用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; }
}
Run Code Online (Sandbox Code Playgroud)

存储库方法

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;
        }
    }
Run Code Online (Sandbox Code Playgroud)

视图

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

现在,如果我删除"用户"导航属性,我将无法执行'@ userteam.User.UserName'

那么,如何重构域模型以删除循环引用,同时能够轻松遍历游戏,并执行类似UserTeam.User.Username的操作?

Hol*_*roe 6

不久前,我对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()
Run Code Online (Sandbox Code Playgroud)