簡體   English   中英

C# 實體框架:創建一對多關系

[英]C# Entity Framework: creating one-to-many relationship

這個問題已經讓我發瘋了好幾天,坦率地說,現在我覺得我一無所知,因為我一直在尋找這么久,嘗試了這么多東西,但都沒有奏效。 所以我想是時候問一個問題了。

我正在嘗試創建一個小型控制台應用程序,將鍛煉動作(Move)作為主要實體。 但是,作為應用程序的一部分,我還希望用戶能夠在名為 MoveRating 的實體中為每次鍛煉留下評分和強度,該實體在獲取 Move 時收集提及的評分。

所以這導致我創建以下設置:

移動.cs

    public class Move
    {
        public int Id { get; set; }
        public string MoveName { get; set; }
        public string MoveDescription { get; set; }
        public int SweatRate { get; set; }
        public ICollection<MoveRating> MoveRating { get; set; }
    }

MoveRating.cs

    public class MoveRating
    {
        public int Id { get; set; }
        public Move Move { get; set; }
        public double Rating { get; set; }
        public double Intensity { get; set; }
    }

現在我知道我應該在我的 DbContext 中做一些事情來實現這一點,我一直在嘗試以下內容:

DbContext(僅 OnModelCreating 部分)

      protected override void OnModelCreating(ModelBuilder builder)
          {
            builder.Entity<MoveRating>().HasOne(m => m.Move).WithMany(a => a.MoveRating);
            builder.Entity<Move>().HasMany(m => m.MoveRating).WithOne(g => g.Move);
          }

我知道它不應該是這樣的,但是無論我嘗試遵循什么例子都是行不通的。 我嘗試過類似的東西:

    builder.Entity<MoveRating>().HasOne(b => b.Move).WithMany(m => m.MoveRating).HasForeignKey(m => m.MoveId);

或者

    builder.Entity<Move>().HasMany(m => m.MoveRating).WithRequired(m => m.MoveRating);

我覺得其中之一應該可以工作。 但無論我嘗試做什么,我似乎都無法讓它發揮作用。 它會給我類似“MoveRating 不包含 MoveId 的定義”或“CollectionNavigationBuilder<Move, MoveRating> 不包含 WithRequired 的定義”之類的消息。

有人可以幫我解決這個問題嗎? 我正在失去理智。

創建一個新目錄,隨意命名。

在目錄中,在命令行中使用以下命令創建一個新的mvc應用程序:

dotnet new mvc --auth Individual  

--auth Individual部分確保您引用了entity framework庫。

在文本編輯器中打開您的應用程序代碼。

要在 EF Core 中使用一個 Move 到多個 MoveRatings 創建一對多關系,您的實體將如下所示(注意[Key]注釋):

public class Move
{
    [Key]
    public int Id { get; set; }
    public string MoveName { get; set; }
    public string MoveDescription { get; set; }
    public int SweatRate { get; set; }
    public List<MoveRating> MoveRating { get; set; }
}

public class MoveRating
{
    [Key]
    public int Id { get; set; }
    public Move Move { get; set; }
    public double Rating { get; set; }
    public double Intensity { get; set; }
}

在您的 DbContext 中,您將有兩個屬性,如下所示:

public DbSet<Move> Moves { get; set; }
public DbSet<MoveRating> MoveRatings { get; set; }

確保您的ConnectionString指向正確的服務器和數據庫。 在大多數情況下,您會在appsettings.js中找到此字符串。

一旦一切看起來都不錯,您將在命令行中執行以下命令(確保您位於項目目錄中):

dotnet ef migrations add InitialCreate

成功運行后,您可以看到一個 Migrations 文件夾,在那里您將看到一個文件名,該文件的名稱以一些數字開頭,后跟 InitialCreate.cs。 你可以打開它,看到它看起來像這樣:

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateTable(
        name: "Moves",
        columns: table => new
        {
            Id = table.Column<int>(type: "INTEGER", nullable: false)
                .Annotation("Sqlite:Autoincrement", true),
            MoveName = table.Column<string>(type: "TEXT", nullable: true),
            MoveDescription = table.Column<string>(type: "TEXT", nullable: true),
            SweatRate = table.Column<int>(type: "INTEGER", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Moves", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "MoveRatings",
        columns: table => new
        {
            Id = table.Column<int>(type: "INTEGER", nullable: false)
                .Annotation("Sqlite:Autoincrement", true),
            MoveId = table.Column<int>(type: "INTEGER", nullable: true),
            Rating = table.Column<double>(type: "REAL", nullable: false),
            Intensity = table.Column<double>(type: "REAL", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_MoveRatings", x => x.Id);
            table.ForeignKey(
                name: "FK_MoveRatings_Moves_MoveId",
                column: x => x.MoveId,
                principalTable: "Moves",
                principalColumn: "Id",
                onDelete: ReferentialAction.Restrict);
        });

    migrationBuilder.CreateIndex(
        name: "IX_MoveRatings_MoveId",
        table: "MoveRatings",
        column: "MoveId");
}

滿意后,在命令行中執行以下命令:

dotnet ef database update

那應該在您的數據庫中創建關系。

筆記

如果您沒有安裝 EF 工具,您可以通過執行以下命令來安裝它們:

dotnet tool install --global dotnet-ef

在你的命令行中。

參考

EF 核心 - 關系: https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key

EF Core - 工具參考: https://docs.microsoft.com/en-us/ef/core/cli/dotnet

我建議您盡可能少地在ModelBuilder的幫助下定義所有關系,因為每次回退數據庫結構時都需要檢查配置文件。 仍然取決於你。

基本上,您只需在MoveRating class 中添加一個int MoveId屬性,刪除您的配置即可。 由於名稱約定,它將獲取Move表。

如果您的數據庫已經存在並且您沒有使用 Code-first + 遷移來更新它,請確保您的 MoveRating 表具有 MoveId 列。 它需要一個。

下一步:對於您的 object 定義,我建議始終將導航屬性聲明為virtual 這可確保延遲加載可用作后備,以避免 null 引用異常之類的事情。 Collections 應自動初始化,以便在創建新父實體時也准備好 go:

public class Move
{
    [Key]
    public int Id { get; set; }
    public string MoveName { get; set; }
    public string MoveDescription { get; set; }
    public int SweatRate { get; set; }
    public virtual ICollection<MoveRating> MoveRating { get; set; } = new List<MoveRating>();
}

public class MoveRating
{
    [Key]
    public int Id { get; set; }
    public virtual Move Move { get; set; }
    public double Rating { get; set; }
    public double Intensity { get; set; }
}

接下來將映射 Move 和 MoveRating 之間的關聯。 您可以將 MoveId 添加到您的 MoveRating 實體中,作為移動的 FK:

public class MoveRating
{
    [Key]
    public int Id { get; set; }
    [ForeignKey("Move")]
    public int MoveId { get; set; }
    public virtual Move Move { get; set; }
    public double Rating { get; set; }
    public double Intensity { get; set; }
}

這很簡單,應該可以幫助您進行操作,但我通常建議您不要這樣做,因為它會為 Move 參考創建兩個事實來源。 (moveRating.MoveId 和 moveRating.Move.Id) 更新 FK 不會自動更新對“Move”的引用,反之亦然。 您可以利用影子屬性來指定 FK,而無需在實體中聲明 FK 字段。 鑒於您使用HasRequired的錯誤,您似乎正在使用 EF Core:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<Move>()
        .HasMany(m => m.MoveRating)
        .WithOne(g => g.Move)
        .HasForeignKey("MoveId");
}

這是您的 MoveRating 實體沒有聲明“MoveId”字段的地方。 EF 將創建一個陰影屬性來表示幕后的 FK 映射。

使用 modelBuilder 或 EntityTypeConfigurations 時,僅 map 從一側而不是兩側輸出關系。 例如,如果您創建一個從 Move 到 MoveRating 的HasMany ,則不要添加另一個從 MoveRating 回到 Move 的映射。 (這不是必需的,它可能會導致錯誤)

最后,在閱讀您的 Move 及其關聯的 MoveRatings collections 時,如果您知道您將需要評級,請在閱讀 Move 實體時立即加載它們。 這通常意味着避免使用像Find這樣的 EF 方法,而使用像Single這樣的 Linq 方法。

var move = context.Moves
    .Include(m => m.MoveRatings)
    .Single(m => m.MoveId == moveId);

這將在一個 SQL 調用中獲取移動/w 它的評級集合。 通過將 MoveRatings 聲明為virtual提供了一個安全網,如果您忘記在 Move 上使用 Include,只要它的 DbContext 引用仍然存在,就可以在訪問時通過延遲加載調用來拉取 MoveRatings。 (盡可能注意和避免的事情,但比觸發 null 引用異常更好)

花了我幾天的時間,但問題的答案非常簡單......

它沒有使用 MoveRatings,因為我試圖在定義 DbContext 的數據訪問層之外訪問它們。僅此而已。

不知道我是否應該保持這個開放的可見性(因為我在任何地方都找不到這個答案)或者我是否應該刪除

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM