Entity Framework Core - 两个实体之间的多个一对多关系

Mr.*_*cky 10 c# database entity-framework-core

我有两个实体 - TeamGame。一个团队可以有很多场比赛(一对多)。

所以这看起来像这样:

 public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public ICollection<Game> Games { get; set; }
    }

 public class Game
    {
        public int Id { get; set; }
        public DateTime Date { get; set; }

        public int TeamId { get; set; }
        public Team Team { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

这很好用,但我想通过将游戏分为两类 - 主场和客场比赛来使其更加精致。然而,这将在两个实体之间引入另一种关系,我不确定如何定义它。

我想它会是这样的吗?

 public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public ICollection<Game> HomeGames { get; set; }
        public ICollection<Game> AwayGames { get; set; }
    }

public class Game
    {
        public int Id { get; set; }
        public DateTime Date { get; set; }

        public int HomeTeamId { get; set; }
        public Team HomeTeam { get; set; }

        public int AwayTeamId{ get; set; }
        public Team AwayTeam { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

这样做会混淆实体框架,并且它无法决定如何解决关系。

有任何想法吗?

Ger*_*old 13

您必须告诉实体框架两个实体中的哪些属性涉及一个关联。在流畅的映射 API 中,这是:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Team>().HasMany(t => t.HomeGames)
        .WithOne(g => g.HomeTeam)
        .HasForeignKey(g => g.HomeTeamId);
    modelBuilder.Entity<Team>().HasMany(t => t.AwayGames)
        .WithOne(g => g.AwayTeam)
        .HasForeignKey(g => g.AwayTeamId).OnDelete(DeleteBehavior.Restrict);
}
Run Code Online (Sandbox Code Playgroud)

您必须使用 fluent API,因为默认情况下,EF 将尝试使用级联删除创建两个外键。SQL Server 不允许这样做,因为它臭名昭著的“多级联路径”限制。其中一个键不应是级联的,只能由 fluent API 进行配置。


Moh*_*hid 5

基于关系 - EF Core | 您可以使用数据批注的Microsoft Docs

数据注释

有两个数据注释可用于配置关系,[ForeignKey] 和 [InverseProperty]。

[外键]

您可以使用数据注释来配置应将哪个属性用作给定关系的外键属性。这通常在外键属性未按惯例发现时完成。

[逆属性]

您可以使用数据注释来配置从属实体和主体实体上的导航属性如何配对。这通常在两种实体类型之间存在一对以上导航属性时完成。

public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }

        [InverseProperty("HomeTeam")]
        public ICollection<Game> HomeGames { get; set; }

        [InverseProperty("AwayTeam")]
        public ICollection<Game> AwayGames { get; set; }
    }

public class Game
    {
        public int Id { get; set; }
        public DateTime Date { get; set; }

        public int HomeTeamId { get; set; }
        [ForeignKey("HomeTeamId")]
        public Team HomeTeam { get; set; }

        public int AwayTeamId{ get; set; }
        [ForeignKey("AwayTeamId")]
        public virtual Team AwayTeam { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

如果你使用 db.Database.Migrate() 你会得到错误

System.Data.SqlClient.SqlException: 表 'Games' 上的'引入 FOREIGN KEY 约束'FK_Games_Teams_HomeTeamId' 可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。无法创建约束或索引。查看以前的错误

你可以让 HomeTeamId AwayTeamId int 吗?可空的

public class Team
    {
        public int Id { get; set; }
        public string Name { get; set; }

        [InverseProperty("HomeTeam")]
        public ICollection<Game> HomeGames { get; set; }

        [InverseProperty("AwayTeam")]
        public ICollection<Game> AwayGames { get; set; }
    }

public class Game
    {
        public int Id { get; set; }
        public DateTime Date { get; set; }

        public int? HomeTeamId { get; set; }
        [ForeignKey("HomeTeamId")]
        public Team HomeTeam { get; set; }

        public int? AwayTeamId{ get; set; }
        [ForeignKey("AwayTeamId")]
        public virtual Team AwayTeam { get; set; }
    }
Run Code Online (Sandbox Code Playgroud)

或参见级联删除 - EF Core | 微软文档

  • 这里是我测试和工作的完整代码(db first not code first

  • 代码首先使用int?

  • 对于 Program.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations.Schema;
    using Microsoft.EntityFrameworkCore;
    
    namespace stackoverflow54196199
    {
    
    public class Team
    {
    
        public int Id { get; set; }
        public string Name { get; set; }
    
        [InverseProperty("HomeTeam")]
        public ICollection<Game> HomeGames { get; set; }
    
        [InverseProperty("AwayTeam")]
        public ICollection<Game> AwayGames { get; set; }
    }
    
    public class Game
    {
        public int Id { get; set; }
        public DateTime Date { get; set; }
    
        public int HomeTeamId { get; set; }
        [ForeignKey("HomeTeamId")]
        public Team HomeTeam { get; set; }
    
        public int AwayTeamId { get; set; }
        [ForeignKey("AwayTeamId")]
        public Team AwayTeam { get; set; }
    }
    
    
    public class MyContext : DbContext
    {
        public DbSet<Game> Games { get; set; }
        public DbSet<Team> Teams { get; set; }
    
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=.;Integrated Security=true;Initial Catalog=stackoverflow54196199;Persist Security Info=False;");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var db = new MyContext();
            foreach (var game in db.Games.Include(i => i.AwayTeam).Include(i => i.HomeTeam))
            {
                Console.WriteLine(game.HomeTeam.Name);
                Console.WriteLine(game.AwayTeam.Name);
    
            }
            Console.ReadLine();
        }
    }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 对于 stackoverflow54196199.csproj

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>netcoreapp2.1</TargetFramework>
    </PropertyGroup>
    
    <ItemGroup>
      <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.1.0" />
      <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.1.0" />
      <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.0" />
     </ItemGroup>
    
    Run Code Online (Sandbox Code Playgroud)