【问题标题】:How to create multiple relations to another class as different properties?如何创建与另一个类的多个关系作为不同的属性?
【发布时间】:2021-01-15 11:31:32
【问题描述】:

我想创建包含多个对象作为与另一个表的关系的类。

例如,如果我想使用不同的对象而不是集合来实现由三名玩家组成的团队,我的代码将如下所示:

public class Team
{
    [Key]
    public int TeamId { get; set; }

    public string Name { get; set; }

    [ForeignKey("Goalkeeper")]
    [Required]
    public int GoalkeeperId { get; set; }
    public virtual Player Goalkeeper { get; set; }

    [ForeignKey("Defender")]
    [Required]
    public int DefenderId { get; set; }
    public virtual Player Defender { get; set; }

    [ForeignKey("Striker")]
    [Required]
    public int StrikerId { get; set; }
    public virtual Player Striker { get; set; }
}

public class Player
{
    [Key]
    public int PlayerId { get; set; }

    public string Name { get; set; }

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

尝试基于此代码创建迁移,但出现错误:

无法确定“团队”类型的导航属性“Player.Team”表示的关系。手动配置关系,或使用“[NotMapped]”属性或使用“OnModelCreating”中的“EntityTypeBuilder.Ignore”忽略此属性。

我错过了什么?这甚至是实现这一点的正确方法吗?

【问题讨论】:

  • stackoverflow.com/questions/5559043/… 看看这个帖子可能会有帮助。
  • stackoverflow.com/questions/10446641/… 两个表不能互相引用,你会先在哪里插入记录。
  • @Nadeem,我见过这个。我认为这个例子有点不同,因为 Team 有一个 Matches 集合。我也从那里尝试了大多数解决方案。如果是表相互引用,我会从 Player 开始,Team 对象在那里是虚拟的,没有它也可以创建。
  • 不要使用两个类,而是使用三个类。在 Player 类中添加一个名为 PlayerType 的列,您可以在其中定义球员是守门员、后卫还是前锋。团队类将具有团队 ID 和团队名称。添加第三个类,例如 TeamDetails,它将具有团队 ID 和玩家 ID。
  • 在这个解决方案中,我没有将球员人数限制为 3。我希望有 3 名球员,一个守门员,一个后卫和一个前锋,可以直接访问 Team.Defender 等。这个解决方案不符合我的标准。

标签: c# entity-framework entity-framework-core


【解决方案1】:

我认为问题在于 EF Core 无法自动检测配置实体之间映射的正确方法。

来自docs

当在两种类型之间定义了多个导航属性时(即不止一对相互指向的导航),导航属性表示的关系是不明确的。您需要手动配置它们以解决歧义。

因此,您应该尝试在您的DbContext 中手动配置它。可能是这样的,但我可能在某个地方有错误,因为我没有测试它。

public class YourDatabaseContext : SqlContext
    {
        ...

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Goalkeeper )
                .WithOne()
                .HasForeignKey<Team>(t => t.GoalkeeperId);

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Defender)
                .WithOne()
                .HasForeignKey<Team>(t => t.DefenderId);

            modelBuilder.Entity<Team>()
                .HasOne(x => x.Striker)
                .WithOne()
                .HasForeignKey<Team>(t => t.StrikerId);
        }
    }

PD:我通常更喜欢这种使用 FluentAPI 配置关系的方法,而不是使用属性污染模型。为了进一步组织代码,您可以为每个放置配置代码的实体创建一个扩展 IEntityTypeConfiguration&lt;SomeEntity&gt; 的类,然后在您的 DbContext.OnModelCreating 中添加此行以扫描您的程序集并添加所有配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
        }

【讨论】:

  • 哇这个好用!谢谢。老实说,当我清楚地将 Id 与虚拟对象相关联时,我仍然不明白为什么它不起作用,我在 OnModelCreating 代码中做的完全一样。感谢那些额外的提示;)跨度>
  • @Kevin 老实说,我也不确定 EF 发现这个模棱两可的确切技术原因是什么,很高兴听到它有帮助:)
猜你喜欢
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-09
  • 2019-07-30
相关资源
最近更新 更多