簡體   English   中英

EF Core - 無法插入具有外鍵的新條目

[英]EF Core - Unable to insert new entry that has a foreign key

我正在嘗試使用 EF Core Code First 在我的Game表中創建新記錄。 它與GenreDeveloperPublisherPlatform共享一對多的關系。

我正在使用一個名為GameCreateViewModel的視圖 model 用於游戲/創建視圖,它包含一個Game屬性以及與每個外鍵對應的 select 列表的屬性,例如List<SelectListItem> Genres

我遇到的問題是當我嘗試創建一個新Game時,它給了我這個錯誤:

Microsoft.EntityFrameworkCore.DbUpdateException
  HResult=0x80131500
  Message=An error occurred while updating the entries. See the inner exception for details.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(DbContext _, Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at GameSource.Data.Repositories.GameRepository.Insert(Game game) in E:\Tom\source\repos\My Projects\GameSource\GameSource.Data\Repositories\GameRepository.cs:line 35
   at GameSource.Services.GameService.Insert(Game game) in E:\Tom\source\repos\My Projects\GameSource\GameSource.Services\GameService.cs:line 29
   at GameSource.Controllers.GamesController.Create(GameCreateViewModel viewModel) in E:\Tom\source\repos\My Projects\GameSource\GameSource\Controllers\GamesController.cs:line 88
   at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeActionMethodAsync()
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeNextActionFilterAsync()

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
SqlException: Cannot insert explicit value for identity column in table 'Developer' when IDENTITY_INSERT is set to OFF.
Cannot insert explicit value for identity column in table 'Genre' when IDENTITY_INSERT is set to OFF.
Cannot insert explicit value for identity column in table 'Platform' when IDENTITY_INSERT is set to OFF.
Cannot insert explicit value for identity column in table 'Publisher' when IDENTITY_INSERT is set to OFF.

游戲model class:

    public class Game
    {
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
        public Genre Genre { get; set; }
        public Developer Developer { get; set; }
        public Publisher Publisher { get; set; }
        public string Description { get; set; }
        public Platform Platform { get; set; }
    }

類型model class:

    public class Genre
    {
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
    }

游戲創建視圖模型

    public class GameCreateViewModel
    {
        public Game Game { get; set; }
        public List<SelectListItem> Genres { get; set; }
        public List<SelectListItem> Developers { get; set; }
        public List<SelectListItem> Publishers { get; set; }
        public List<SelectListItem> Platforms { get; set; }
    }

游戲/創建視圖- 僅類型 select 列表的代碼,其他 select 列表重復相同的格式:

@model GameSource.ViewModels.GameViewModel.GameCreateViewModel

        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Game.ID" />
            <div class="form-group">
                <label asp-for="Game.Name" class="control-label"></label>
                <input asp-for="Game.Name" class="form-control" />
                <span asp-validation-for="Game.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Game.Genre" class="control-label"></label>
                <select asp-for="Game.Genre.ID" asp-items="@Model.Genres" class="form-control">
                    <option value="">Select a Genre/Genres</option>
                </select>
                <span asp-validation-for="Game.Genre" class="text-danger"></span>
            </div>

游戲/創建 Controller

        [HttpGet]
        public IActionResult Create()
        {
            GameCreateViewModel viewModel = new GameCreateViewModel();
            viewModel.Game = new Game();
            viewModel.Genres = genreService.GetAll().Select(x => new SelectListItem()
            {
                Text = x.Name,
                Value = x.ID.ToString()
            }).ToList();
            viewModel.Developers = developerService.GetAll().Select(x => new SelectListItem()
            {
                Text = x.Name,
                Value = x.ID.ToString()
            }).ToList();
            viewModel.Publishers = publisherService.GetAll().Select(x => new SelectListItem()
            {
                Text = x.Name,
                Value = x.ID.ToString()
            }).ToList();
            viewModel.Platforms = platformService.GetAll().Select(x => new SelectListItem()
            {
                Text = x.Name,
                Value = x.ID.ToString()
            }).ToList();

            return View(viewModel);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public IActionResult Create(GameCreateViewModel viewModel)
        {
            Game game = new Game
            {
                ID = viewModel.Game.ID,
                Name = viewModel.Game.Name,
                Genre = viewModel.Game.Genre,
                Developer = viewModel.Game.Developer,
                Publisher = viewModel.Game.Publisher,
                Description = viewModel.Game.Description,
                Platform = viewModel.Game.Platform
            };
            gameService.Insert(game);
            return RedirectToAction("Index");
        }

似乎它也在嘗試為外鍵插入一個新條目,即使我只是想將現有 ID 用於新的Game條目。 例如,新Game的 GenreID 為 1,因此它應該引用 ID 為 1 的現有 Genre 條目。

對此的任何見解都非常感謝。 另外,如果您也需要查看服務和回購方法,請告訴我。 謝謝你的時間。

我最終在我的游戲 class 以及我的 GameCreateViewModel 中添加了外鍵的 id 屬性,並重新更新了我的數據庫。 它允許我正確地創建一個新游戲,例如在 controller 中為新游戲分配一個流派時,我能夠根據視圖模型的 Game.GenreID 屬性返回一個流派。

流派示例 - 相同的想法適用於其他外鍵:

游戲創建視圖模型

    public class GameUpdateViewModel
    {
        public Game Game { get; set; }
        public int GenreID { get; set; }
        public List<SelectListItem> Genres { get; set; }
    

游戲/創建 controller 方法

Game game = new Game 
{    
ID = viewModel.Game.ID, 
Name = viewModel.Game.Name, 
Description = viewModel.Game.Description, 
GenreID = viewModel.Game.GenreID

在視圖中 - 在 select 列表中,它使用的是 Game.GenreID

<select asp-for="Game.GenreID" asp-items="@Model.Genres" class="form-control"> 
<option value="">Select a Genre/Genres</option> 
</select> 

這個想法是存在外鍵屬性,否則我無法將現有的外鍵 ID 分配給新游戲。

您的游戲實體的表示應該是這樣的:

public class Game
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public int GenreId { get; set; }
    public int DeveloperId { get; set; }
    public int PublisherId { get; set; }
    public int PlatformId { get; set; }

    public Genre Genre { get; set; }
    public Developer Developer { get; set; }
    public Publisher Publisher { get; set; }        
    public Platform Platform { get; set; }
}

由於 EF 跨外鍵引用您的一側實體,因此您應該在您的上下文中配置它。

        builder.HasKey(g => g.ID);
        builder.Property(g => g.Name);
        builder.Property(g => g.Description);

        builder.Property(g => g.GenreId).HasColumnName("GenreID");

        builder.HasOne(g => g.Genre)
            .WithMany(h => h.Games)
            .HasForeignKey(g => g.GenreId)
            .OnDelete(DeleteBehavior.ClientSetNull)
            .HasConstraintName("FK_Games_Genre");  // Repeat the last two lines for all your one side entity relationships

現在,當需要添加游戲實體時,您只需放置外鍵。 例如:

new Game({
   Name = "GameName"
   .
   .
   GenreId = 1
   .
   .
});

只需將游戲添加到上下文並保存即可。 EF 將以正確的方式 map 您的實體關系。

注意:請注意您的 EF 可能會有所不同。

如果您是 EF Core 的新手,我建議您在完成 DB 架構后使用 EF Database-First 方法,這將為項目帶來上下文所需的所有配置。

您必須更改代碼

public class Game
{
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
        public int GenreID { get; set; }
        public int DeveloperID { get; set; }
        public int PublisherID { get; set; }
        public string Description { get; set; }
        public int PlatformID { get; set; }
}


 <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Game.ID" />
            <div class="form-group">
                <label asp-for="@Model.GameName" class="control-label"></label>
                <input asp-for="@Model.GameName" class="form-control" />
                <span asp-validation-for="@Model.GameName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label>Platform</label>
         <select asp-for="@Model.PlatformID"
            asp-items="@(new SelectList(Model.Platforms,"Id","Name"))">
        <option>Please select one</option>
    </select>

                
  </div>
 <div class="form-group">
                <label>Publisher</label>
         <select asp-for="@Model.PublisherID"
            asp-items="@(new SelectList(Model.Publishers,"Id","Name"))">
        <option>Please select one</option>
    </select>

                
  </div>
<div class="form-group">
                <label>Publisher</label>
         <select asp-for="@Model.PublisherID"
            asp-items="@(new SelectList(Model.Publishers,"Id","Name"))">
        <option>Please select one</option>
    </select>

                
  </div>
 <div class="form-group">
                <label>Developer</label>
         <select asp-for="@Model.DeveloperID"
            asp-items="@(new SelectList(Model.Developers,"Id","Name"))">
        <option>Please select one</option>
    </select>
  </div>

public class GameCreateViewModel
{
    public String GameName { get; set; }
    public int GenreID { get; set; }
    public int DeveloperID { get; set; }
    public int PublisherID { get; set; }
    public int PlatformID { get; set; }

    public Game Game { get; set; }
    public List<Genre> Genres { get; set; }
    public List<Developer> Developers { get; set; }
    public List<Publisher> Publishers { get; set; }
    public List<Platform> Platforms { get; set; }
}

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(GameCreateViewModel viewModel)
{
    Game game = new Game
            {
                Name = viewModel.GameName,
                GenreID = viewModel.GenreID,
                DeveloperID = viewModel.DeveloperID,
                Publisher = viewModel.PublisherID,
                Description = viewModel.Game.Description,
                Platform = viewModel.PlatformID
            };
    gameService.Insert(game);

    return RedirectToAction("Index");
}

public class Developer
{
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
}

public class Publisher
{
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
}

public class Platform
{
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
}

暫無
暫無

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

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