首先,我只想说我是一个 ASP .NET Core MVC 和实体框架的新手,所以我真的不知道我这样做是否正确或以最好的方式。
我正在尝试做一个简单的程序,你可以在其中注册游戏,每个游戏都有一些关于它们的信息,与它们关联的控制台,以及我无法解决的问题,与一种或多种类型的关联。
但是从简单开始,我只想注册一个只有一种类型的游戏(而且我还不知道如何显示多种类型以供选择)。
游戏和控制台信息在数据库中注册没有问题,但是当我尝试注册具有流派的游戏时,出现此错误:
处理请求时发生未处理的异常。
InvalidOperationException:尝试保存更改时,“GameGameGenre.GameId”的值未知。这是因为该属性也是外键的一部分,关系中的主体实体未知。
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.PrepareToSave()
这些是这个问题的相关模型(至少我认为)
这是游戏类:
namespace ProjectC01.Models
{
public class Game
{
public int GameId { get; set; }
public string GameName { get; set; }
public string GameDeveloper { get; set; }
public string GamePublisher { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime GameRelease { get; set; }
public string GameCover { get; set; }
public string ImageLocation { get; set; }
public VideoGameConsole GameConsole { get; set; }
public int VideoGameConsoleId { get; set; }
public ICollection<GameGenre> GameGenres { get; set; } = new List<GameGenre>();
public ICollection<GameGameGenre> GameGameGenres { get; set; } = new List<GameGameGenre>();
public Game() { }
public Game(int gameId, string gameName, string gameDeveloper,
string gamePublisher, DateTime gameRelease, string gameCover, VideoGameConsole gameConsole, string imageLocation)
{
GameId = gameId;
GameName = gameName;
GameDeveloper = gameDeveloper;
GamePublisher = gamePublisher;
GameRelease = gameRelease;
GameCover = gameCover;
GameConsole = gameConsole;
ImageLocation = imageLocation;
}
}
}
这是游戏类型类:
namespace ProjectC01.Models
{
public class GameGenre
{
[Key]
public int GenreId { get; set; }
public string GenreName { get; set; }
public ICollection<Game> Games { get; set; } = new List<Game>();
public ICollection<GameGameGenre> GameGameGenres { get; set; } = new List<GameGameGenre>();
public GameGenre() { }
public GameGenre(int genreId, string genreName)
{
GenreId = genreId;
GenreName = genreName;
}
}
}
这个类负责游戏和类型之间的连接
namespace ProjectC01.Models
{
public class GameGameGenre
{
public Game Game { get; set; }
public int GameId { get; set; }
public GameGenre GameGenre { get; set; }
public int GameGenreId { get; set; }
public GameGameGenre() { }
public GameGameGenre(Game game, int gameId, GameGenre gameGenre, int gameGenreId)
{
Game = game;
GameId = gameId;
GameGenre = gameGenre;
GameGenreId = gameGenreId;
}
}
}
这是游戏的控制器
namespace ProjectC01.Controllers
{
public class GameController : Controller
{
private readonly GameService _gameService;
private readonly VideoGameConsoleService _videoGameConsoleService;
private readonly GameGenreService _gameGenreService;
private readonly ProjectC01Context _context;
private readonly IWebHostEnvironment _environment;
public GameController(GameService gameService, VideoGameConsoleService videoGameConsoleService,
GameGenreService gameGenreService, ProjectC01Context context, IWebHostEnvironment environment)
{
_gameService = gameService;
_videoGameConsoleService = videoGameConsoleService;
_gameGenreService = gameGenreService;
_context = context;
_environment = environment;
}
public IActionResult Index()
{
var list = _gameService.FindAll();
return View(list);
}
public async Task<IActionResult> Create()
{
var consoles = await _videoGameConsoleService.FindAllAsync();
var genres = await _gameGenreService.FindAllAsync();
var viewModel = new GameFormViewModel { Consoles = consoles, GameGenres = genres };
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(GameFormViewModel model, IFormFile imageUpload)
{
if (imageUpload == null || imageUpload.Length == 0)
{
return Content("Nenhuma imagem selecionada");
}
var cover = Path.Combine(_environment.WebRootPath, "Images\\GameCovers", imageUpload.FileName);
using (FileStream stream = new FileStream(cover, FileMode.Create))
{
await imageUpload.CopyToAsync(stream);
stream.Close();
}
string imgPath = "\\Images\\GameCovers\\" + imageUpload.FileName;
model.Game.GameCover = imageUpload.FileName;
if (model != null)
{
var game = new Game
{
GameName = model.Game.GameName,
GameDeveloper = model.Game.GameDeveloper,
GamePublisher = model.Game.GamePublisher,
GameRelease = model.Game.GameRelease,
GameCover = model.Game.GameCover,
VideoGameConsoleId = model.Game.VideoGameConsoleId,
ImageLocation = imgPath,
};
var gameGameGenre = new GameGameGenre
{
GameId = model.GameGameGenre.GameId,
GameGenreId = model.GameGameGenre.GameGenreId,
};
_context.Add(game);
_context.Add(gameGameGenre);
await _context.SaveChangesAsync(); //The error occurs after this line is executed
}
return RedirectToAction("Index");
}
}
}
这是游戏的创建视图
@model ProjectC01.Models.ViewModels.GameFormViewModel
@{
ViewData["Title"] = "Cadastrar";
}
<h1>@ViewData["Title"]</h1>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create" method="post" enctype="multipart/form-data">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Game.GameName" class="control-label"></label>
<input asp-for="Game.GameName" class="form-control" />
<span asp-validation-for="Game.GameName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Game.GameDeveloper" class="control-label"></label>
<input asp-for="Game.GameDeveloper" class="form-control" />
<span asp-validation-for="Game.GameDeveloper" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Game.GamePublisher" class="control-label"></label>
<input asp-for="Game.GamePublisher" class="form-control" />
<span asp-validation-for="Game.GamePublisher" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Game.GameRelease" class="control-label"></label>
<input asp-for="Game.GameRelease" class="form-control" />
<span asp-validation-for="Game.GameRelease" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Game.GameCover" class="control-label"></label>
<input asp-for="Game.GameCover"
type="file" name="imageUpload" accept="image/*" class="form-control" />
<span asp-validation-for="Game.GameCover" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Game.VideoGameConsoleId" class="control-label"></label>
<select asp-for="Game.VideoGameConsoleId" class="form-select"
asp-items="@(new SelectList(Model.Consoles,"ConsoleId","ConsoleName"))"></select>
</div>
<div class="form-group">
<label asp-for="GameGameGenre.GameGenreId" class="control-label"></label>
<select asp-for="GameGameGenre.GameGenreId" class="form-select"
asp-items="@(new SelectList(Model.GameGenres,"GenreId","GenreName"))"></select>
</div>
<hr />
<div class="form-group">
<input type="submit" value="Cadastrar" class="btn btn-primary" /> |
<a asp-action="Index" class="btn btn-secondary">Retornar a Lista</a>
</div>
</form>
</div>
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
}
这是上面CREATE VIEW中使用的VIEWMODEL
namespace ProjectC01.Models.ViewModels
{
public class GameFormViewModel
{
public Game Game { get; set; }
public GameGenre GameGenre { get; set; }
public GameGameGenre GameGameGenre { get; set; }
public IFormFile ImageUpload { get; set; }
public ICollection<VideoGameConsole> Consoles { get; set; }
public ICollection<GameGenre> GameGenres { get; set; }
}
}
这是应用程序的上下文
namespace ProjectC01.Data
{
public class ProjectC01Context : DbContext
{
public ProjectC01Context(DbContextOptions<ProjectC01Context> options)
: base(options) { }
public DbSet<VideoGameConsole>? VideoGameConsole { get; set; }
public DbSet<GameGenre>? GameGenre { get; set; }
public DbSet<Game>? Game { get; set; }
public DbSet<GameGameGenre>? GameGameGenre { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Game>()
.HasMany(e => e.GameGenres)
.WithMany(e => e.Games)
.UsingEntity<GameGameGenre>(
r => r.HasOne<GameGenre>(e => e.GameGenre).WithMany(e => e.GameGameGenres),
l => l.HasOne<Game>(e => e.Game).WithMany(e => e.GameGameGenres));
modelBuilder.Entity<GameGenre>()
.HasMany(e => e.Games)
.WithMany(e => e.GameGenres)
.UsingEntity<GameGameGenre>(
r => r.HasOne<Game>(e => e.Game).WithMany(e => e.GameGameGenres),
l => l.HasOne<GameGenre>(e => e.GameGenre).WithMany(e => e.GameGameGenres));
//I don't really know if it was necessary to do this for both classes
}
}
}
我认为这就是以某种方式与问题相关的所有事情,可能我在 CREATE 视图或控制器上的 CREATE POST 操作中做错了什么,至少这是我得出的结论。
我搜索了很多可能有同样问题的帖子,但我真的找不到任何,抱歉,如果我不够清楚,但正如我所说,我刚刚开始使用这个asp .net MVC/EF 的东西。
感谢 GitHub 上的这个问题,我找到了这个问题的解决方案。
问题是因为代码从未为 GameId 设置 id 值,因为它是数据库上的自动增量 PK,因此当控制器尝试将 GameId 添加到 GameGameGenre 表时,它会变为 0,导致上面的错误。
我解决这个问题的方法是首先将游戏包含在数据库中(就像我已经在做的那样),然后他们在数据库上对刚刚注册的游戏进行 LINQ 搜索,获取其 id,并将其用于 GameGameGenre.GameId插入。
public async Task CreateNewGGS(GameFormViewModel model)
{
if (model != null)
{
var gameId = _context.Game.SingleOrDefault(m =>
m.GameName == model.Game.GameName &&
m.GameDeveloper == model.Game.GameDeveloper &&
m.GamePublisher == model.Game.GamePublisher &&
m.GameRelease == model.Game.GameRelease &&
m.GameCover == model.Game.GameCover &&
m.VideoGameConsoleId == model.Game.VideoGameConsoleId
).GameId;
var ggs = new GameGameGenre
{
GameId = gameId,
GameGenreId = model.GameGameGenre.GameGenreId,
};
await InsertAsync(ggs);
}
}