0

在试图弄清楚如何使用 Code First 在实体框架中实现双向 1..n 关系时,我感到非常困惑。例如,一个团队(由一个 Team 实体表示)有一个教练和一个经理(都由一个 Person 实体表示)。所以,我的团队模型可能如下:

public class Team
{
    public Team()
    {
        Manager = new Person();
        Coach = new Person();
    }

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int TeamID { get; set; }
    public string Name { get; set; }

    [ForeignKey("Manager")]
    public int ManagerID { get; set; }
    public virtual Person Manager { get; set; }

    [ForeignKey("Coach")]
    public int CoachID { get; set; }
    public virtual Person Coach { get; set; }
}

我可以通过实现 Person Entity 来实现单向导航,如下所示:

public class Person
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PersonID { get; set; }
    public string Name { get; set; }
}

和流畅的 API:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Team>()
        .HasRequired(t => t.Manager);

    modelBuilder.Entity<Team>()
        .HasRequired(t => t.Coach);

    base.OnModelCreating(modelBuilder);
}

但是,虽然这允许我从 Team 实体导航到相关的 Coach 和 Manager(两个 Person 实例),但它不允许我直接从 Coach 或 Manager 导航到相关的 Team。所以,为了实现双向导航,我修改了 Person 实体如下:

public class Person
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PersonID { get; set; }
    public string Name { get; set; }

    [ForeignKey("Team")]
    public int TeamID { get; set; }
    public virtual Team Team { get; set; }
}

虽然构建正常,但当我尝试保存到数据库时出现以下运行时错误:

System.Data.Entity.Core.UpdateException
InnerException: Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

因此,为了指定实体之间的顺序,我尝试通过添加“WithRequiredPricipal”来修改流畅的 API,如下所示:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Team>()
        .HasRequired(t => t.Manager)
        .WithRequiredPrincipal(t => t.Team);

    modelBuilder.Entity<Team>()
        .HasRequired(t => t.Coach)
        .WithRequiredPrincipal(t => t.Team);

    base.OnModelCreating(modelBuilder);
}

但是,当我尝试在包管理器控制台中执行“添加迁移”时,我收到以下错误:

System.InvalidOperationException: The navigation property 'Team' declared on type 'RelatedEntities.Models.Person' has been configured with conflicting foreign keys.

我试图实现的目标似乎是一个简单的要求,但我已经做了很多寻找解决方案的工作,但还没有找到答案。我在流畅的 API 或注释中遗漏了什么吗?

(我不想实现解决方法,例如为教练和经理实现单独的模型,因为一个团队可能有许多其他角色(例如,助理教练、公共关系经理等)。我希望每个角色都只是一个实例个人实体。)

4

2 回答 2

0

您没有定义外键约束。

.HasRequired(t => t.Manager)
.WithMany()
.HasForeignKey(t => t.ManagerId)

作为旁注。如果您的经理管理多个团队,WithMany那么.WithMany(m => m.Teams)您的经理模型将需要这样的东西:

private ICollection<Team> _teams
public ICollection<Team> Teams 
{
    get { return _teams ?? (teams = new List<Team>()); }
    protected set { _teams = value; }
}

抱歉格式化。在我的手机上。祝你好运。

于 2014-09-03T04:52:32.570 回答
0

好的,我现在可以回答我自己的问题,以防其他人遇到同样的问题。

首先,正如我在上面的评论中提到的那样,我在问题中描述的问题原来是实体框架中公认的缺陷,EF 分类团队已将其标记为在未来的 EF 版本中解决。

同时,许多贡献者在回答有关如何在 EF 中实现循环依赖的问题(这是我上面的示例试图做的)时建议的解决方法是分阶段进行,如下所示:

  1. 创建主体实体
  2. 在 DbContext 上调用 SaveChanges()
  3. 在再次调用 SaveChanges() 之前创建依赖实体并设置外键(在随后的某个时间点)

因此,使用上面我原来问题中的 Team 示例,而不是在 Team 构造函数中创建依赖的 Manager 和 Coach 实体,第一个更改是使 Team 对 Coach 和 Manager 的外键是可选的(而不是必需的),这样Team 可以在没有 Coach 和 Manager 的情况下实例化,然后调用 SaveChanges():

Team team = Teams.Add(new Team());
SaveChanges();

之后,我创建了依赖的 Manager 和 Coach 实体,并将它们的外键设置为 Team 实例的 ID:

team.Manager = new Person();
team.Manager.TeamID = team.TeamID;

team.Coach = new Person();
team.Coach.TeamID = team.TeamID;

在此之后的任何时候,都可以调用 SaveChanges() 而不会像以前那样导致运行时错误。

当这个问题在 EF 的未来版本中得到解决时,应该可以创建主体实体和依赖实体,而不必在两者之间调用 SaveChanges()。

于 2014-09-05T10:13:23.673 回答