【问题标题】:EF Core - Unable to insert new entry that has a foreign keyEF Core - 无法插入具有外键的新条目
【发布时间】:2020-07-12 20:56:45
【问题描述】:

我正在尝试使用 EF Core Code First 在我的 Game 表中创建一条新记录。它与GenreDeveloperPublisherPlatform 共享一对多关系。

我正在为游戏/创建视图使用一个名为 GameCreateViewModel 的视图模型,它包含一个 Game 属性以及与每个外键对应的选择列表的属性,例如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.

游戏模型类:

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

体裁模型类:

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

GameCreateViewModel

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

游戏/创建视图 - 仅限类型选择列表的代码,其他选择列表重复相同的格式:

@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>

游戏/创建控制器

        [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 条目。

非常感谢您对此的任何见解。另外,如果您也需要查看服务和回购方法,请告诉我。感谢您的宝贵时间。

【问题讨论】:

标签: c# asp.net-core entity-framework-core


【解决方案1】:

我最终在我的 Game 类和 GameCreateViewModel 中添加了外键的 id 属性,并重新更新了我的数据库。它允许我正确地创建一个新游戏,例如在控制器中为新游戏分配一个流派时,我能够根据视图模型的 Game.GenreID 属性返回一个流派。

以流派为例——同样的想法也适用于其他外键:

GameCreateViewModel

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

游戏/创建控制器方法

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

在视图中 - 在选择列表中,它使用 Game.GenreID

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

这个想法是让外键属性存在,否则我无法将现有的外键 ID 分配给新游戏。

【讨论】:

    【解决方案2】:

    你的游戏实体的表示应该是这样的:

    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 将以正确的方式映射您的实体关系。

    注意事项: 请注意您的 EF 可能会有所不同。

    如果您是 EF Core 新手,我建议您在完成 DB 架构后使用 EF Database-First 方法,这将为项目带来上下文所需的所有配置。

    【讨论】:

      【解决方案3】:

      你必须改变代码

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

      【讨论】:

      • 请解释你的答案。您是否正在尝试将子 ID 映射到外键?
      • @tomtomdam 你在哪里遇到代码问题?
      猜你喜欢
      • 2023-03-17
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-15
      • 2020-07-24
      相关资源
      最近更新 更多