在Entity Framework中创建没有循环引用的域模型 [英] Creating a domain model without circular references in Entity Framework

查看:94
本文介绍了在Entity Framework中创建没有循环引用的域模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了一个可行的解决方案(使用DTO和AutoMapper),下面将对此进行复制,但是我希望给出一个答案,其中列出了解决问题的不同方法,并带有示例,并且

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.

在我的实体模型中,我具有从子实体到父实体的导航属性。我的项目正在顺利进行。然后我开始使用AutoFixture进行单元测试,但测试失败,AutoFixture说我有一个循环引用。

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.

现在,我意识到像这样的循环引用导航属性是可以的在Entity Framework中,但我发现了这篇文章(在AutoFixture中创建复杂子级时使用父属性的值),其中AutoFixture的创建者Mark Seemann指出:

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:

对于从记录来看,多年以来我都没有写过带有循环引用的API,因此很有可能避免那些父子关系。

"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.

下面是有问题的实体类,存储库方法以及如何使用导致View中循环引用的属性。 完美的答案将说明我可以从示例中选择的不同选项,以及每种方法的基本优点/缺点。

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.

注意:导致循环引用的属性是UserTeam模型中的User。

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

模型:

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'

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

因此,如何重构域模型以删除循环引用,同时又能够轻松遍历游戏,并执行类似
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?

推荐答案

我有一个类似问题。我的解决方案是为AutoFixture添加一个扩展,该扩展使您可以构建具有一些递归的SUT。该扩展最近已在AutoFixture中采用。

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.

但是我知道您的问题不是关于如何使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.

首先,您具有树或图结构。在这里,除了递归以外,其他任何东西都意味着通过松散耦合的节点ID进行间接访问。除了定义关联之外,您还必须遍历查询树或缓存整个对象,然后按节点键查找遍历树,这可能不切实际,具体取决于树的大小。在这里让EF为您完成工作非常方便。

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()

这篇关于在Entity Framework中创建没有循环引用的域模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆