[英]The INSERT statement conflicted with the FOREIGN KEY using EF Core
[英]EF Core - Unable to insert new entry that has a foreign key
我正在尝试使用 EF Core Code First 在我的Game
表中创建新记录。 它与Genre
、 Developer
、 Publisher
和Platform
共享一对多的关系。
我正在使用一个名为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.