“InvalidOperationException:”尝试将多对多关系保存到数据库时

问题描述 投票:0回答:1

首先,我只想说我是一个 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 的东西。

c# asp.net-mvc asp.net-core many-to-many .net-6.0
1个回答
0
投票

感谢 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);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.