简体   繁体   English

EF Core - 无法插入具有外键的新条目

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

I am trying to create a new record in my Game table using EF Core Code First.我正在尝试使用 EF Core Code First 在我的Game表中创建新记录。 It shares one-to-many relationships with Genre , Developer , Publisher and Platform .它与GenreDeveloperPublisherPlatform共享一对多的关系。

I am using a view model called GameCreateViewModel for the Game/Create View which holds a Game property as well as properties for select lists that correspond with each foreign key eg List<SelectListItem> Genres .我正在使用一个名为GameCreateViewModel的视图 model 用于游戏/创建视图,它包含一个Game属性以及与每个外键对应的 select 列表的属性,例如List<SelectListItem> Genres

The problem I am having is when I attempt to create a new Game , it gives me this error:我遇到的问题是当我尝试创建一个新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.

Game model class:游戏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; }
    }

Genre model class:类型model class:

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

Game/Create View - code of Genre select list only, the same format repeated for the other select lists:游戏/创建视图- 仅类型 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>

Game/Create Controller :游戏/创建 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");
        }

It seems like it is trying to insert a new entry for the foreign keys as well, even though I am simply wanting to use existing IDs for the new Game entry.似乎它也在尝试为外键插入一个新条目,即使我只是想将现有 ID 用于新的Game条目。 Eg The new Game has a GenreID of 1, so it should be referring to the existing Genre entry with an ID of 1.例如,新Game的 GenreID 为 1,因此它应该引用 ID 为 1 的现有 Genre 条目。

Any insight into this is very much appreciated.对此的任何见解都非常感谢。 Also, please let me know if you need to see the service and repo methods too.另外,如果您也需要查看服务和回购方法,请告诉我。 Thanks for your time.谢谢你的时间。

I ended up adding the id properties for the foreign keys in my Game class as well as in my GameCreateViewModel, and re-updated my database.我最终在我的游戏 class 以及我的 GameCreateViewModel 中添加了外键的 id 属性,并重新更新了我的数据库。 It allowed me to create a new Game properly, for example when assigning a Genre to the new Game in the controller, I was able to return a Genre based on the view model's Game.GenreID property.它允许我正确地创建一个新游戏,例如在 controller 中为新游戏分配一个流派时,我能够根据视图模型的 Game.GenreID 属性返回一个流派。

Example with Genre - the same idea applies for other foreign keys:流派示例 - 相同的想法适用于其他外键:

GameCreateViewModel :游戏创建视图模型

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

Game/Create controller method :游戏/创建 controller 方法

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

And in the view - in the select list it's using Game.GenreID instead :在视图中 - 在 select 列表中,它使用的是 Game.GenreID

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

The idea is to have the foreign key properties present, otherwise I'm not able to assign an existing foreign key ID to the new Game.这个想法是存在外键属性,否则我无法将现有的外键 ID 分配给新游戏。

The representtion of your Game entity should be something like this:您的游戏实体的表示应该是这样的:

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

Since EF refers to your One side entity across the foreign key, you should configure it in your context.由于 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

Now you would put just the foreign key when need to add a Game entity.现在,当需要添加游戏实体时,您只需放置外键。 For instance:例如:

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

Just add game to the context and save.只需将游戏添加到上下文并保存即可。 EF will map your entities relationships in the correct way. EF 将以正确的方式 map 您的实体关系。

Notes: Please considere that your EF could be different.注意:请注意您的 EF 可能会有所不同。

If you are new to EF Core I suggest to use EF Database-First approach once you did the DB schema and this will bring to the project all the config that the context needs.如果您是 EF Core 的新手,我建议您在完成 DB 架构后使用 EF Database-First 方法,这将为项目带来上下文所需的所有配置。

You have to change around the code您必须更改代码

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