实体框架核心-两个实体之间的多个一对多关系

时间:2019-01-15 09:40:07

标签: c# database entity-framework-core

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

所以看起来像这样:

The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.

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

我想会是这样吗?

 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; }
    }

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

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

基于Relationships - EF Core | Microsoft Docs,您可以使用数据注释

  

数据注释

     

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

     

[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; }
    }

如果使用db.Database.Migrate(),则会出现错误提示

  

System.Data.SqlClient.SqlException:'引入外键   表“游戏”上的约束“ 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; }
    }

或参见Cascade Delete - EF Core | Microsoft Docs

  • 这里是我测试和工作的完整代码(先而不是先编码)

  • 对于代码,请先使用 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();
        }
    }
    }
    
  • 对于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>
    

答案 1 :(得分:2)

您必须告诉Entity Framework一个关联中涉及两个实体中的哪些属性。在流利的Mapping 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);
}

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