繁体   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