ASP.NET Core中一对多或多对一的关系数据未插入。

huangapple go评论59阅读模式
英文:

ASP.NET Core one-to-many or many-to-one relationship data is not inserted

问题

以下是翻译好的部分:

我尝试开发一个简单的博客应用程序,以学习 ASP.NET Core。每篇文章都应该有一个类别,并且在创建文章页面上应该能够选择该类别。我使用了 `DropDownListFor` HTML 辅助程序来呈现类别,所有类别都在创建文章页面上呈现。我分享下面的 `CreateModel` 类:

以下是翻译好的部分:

我分享下面的 `Create.cshtml` 文件:

当我保存这篇文章时,会出现以下异常:

在处理请求时发生未处理的异常。
SqlException: 当 IDENTITY_INSERT 设置为 OFF 时,无法在表 'Category' 中为标识列插入显式值。
Microsoft.Data.SqlClient.SqlCommand+<>c.<ExecuteDbDataReaderAsync>b__208_0(Task<SqlDataReader> result)

DbUpdateException: 保存实体更改时发生错误。有关详细信息,请参见内部异常。
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)

这个异常有两个可能的原因:

  1. 可能在类别表中无法插入某些数据,但我没有从服务器请求它们。
  2. 可能不能直接在文章表中插入 categoryId。
英文:

I try to develop a simple blog app for learning ASP.NET Core. Every Post should have a category and this category should be selectable on creating a post page. I use to DropDownListFor HTML helper for rendering categories and all category is rendered on creating a post page. I shared below the CreateModel class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using netblogapp.Data;
using netblogapp.Models;

namespace netblogapp.Pages.Admin.Posts
{
    public class CreateModel : PageModel
    {
        private readonly netblogapp.Data.BlogAppDbContext _context;

        public CreateModel(netblogapp.Data.BlogAppDbContext context)
        {
            _context = context;
        }

        [BindProperty]
        public netblogapp.Models.Post Post { get; set; } = default!;

        public IList<netblogapp.Models.Category> Categories { get; set; }

        public async Task OnGetAsync()
        {
            if (_context.Category != null)
            {
                this.Categories = await _context.Category.ToListAsync();
            }
        }

        // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
        public async Task<IActionResult> OnPostAsync()
        {
          if (!ModelState.IsValid || _context.Post == null || Post == null)
            {
                return Page();
            }

            _context.Post.Add(Post);
            await _context.SaveChangesAsync();

            return RedirectToPage("./Index");
        }
    }
}

I shared below the Create.cshtml file:

@page
@model netblogapp.Pages.Admin.Posts.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Post</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Post.Title" class="control-label"></label>
                <input asp-for="Post.Title" class="form-control" />
                <span asp-validation-for="Post.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Post.Description" class="control-label"></label>
                <input asp-for="Post.Description" class="form-control" />
                <span asp-validation-for="Post.Description" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Post.Author" class="control-label"></label>
                <input asp-for="Post.Author" class="form-control" />
                <span asp-validation-for="Post.Author" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Post.CreatedAt" class="control-label"></label>
                <input asp-for="Post.CreatedAt" class="form-control" />
                <span asp-validation-for="Post.CreatedAt" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Post.UpdatedAt" class="control-label"></label>
                <input asp-for="Post.UpdatedAt" class="form-control" />
                <span asp-validation-for="Post.UpdatedAt" class="text-danger"></span>
            </div>
            <div class="form-group form-check">
                <label class="form-check-label">
                    <input class="form-check-input" asp-for="Post.isPublish" /> @Html.DisplayNameFor(model => model.Post.isPublish)
                </label>
            </div>
            <div class="form-group">
                <label asp-for="Post.Context" class="control-label"></label>
                <input asp-for="Post.Context" class="form-control" />
                <span asp-validation-for="Post.Context" class="text-danger"></span>
            </div>

            @Html.DropDownListFor( m => m.Post.Category.Id, new SelectList(Model.Categories, "Id", "Name"), "--- Select Category ---")

            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

When I save this post, this exception is thrown:

An unhandled exception occurred while processing the request.
SqlException: Cannot insert explicit value for identity column in table 'Category' when IDENTITY_INSERT is set to OFF.
Microsoft.Data.SqlClient.SqlCommand+<>c.<ExecuteDbDataReaderAsync>b__208_0(Task<SqlDataReader> result)

DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)

There can be two reasons for this exception:

  1. Some data can not insert on the category table but I didn't request from the server about it
  2. Maybe I can't directly insert categoryId on the Post table.

答案1

得分: 0

你可以使用属性:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

来标记 Category 对象的 Key。

英文:

You can use attribute:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]

to Key of Category object.

答案2

得分: 0

我通过以下几个步骤解决了这个问题:

1- 向Post类添加了CategoryId变量。在不对帖子表进行任何更改的情况下,EF执行了这个操作。这是我更新后的Post类:

public class Post
{
    public int Id { get; set; }
    // 其他变量
    public int CategoryId { get; set; } // 后来添加的变量
    public Category? Category { get; set; }
}

然后,我将m.Post.Category.Id的HTML助手LINQ语句更改为m.CategoryId

2- OnGetAsync方法更改为OnGet()。尽管在网页上呈现所有类别,但异步方法导致Category模型为null。这是在create.cshtml上的条件语句中确定的。

@if (Model.Categories != null)
{
  @Html.DropDownListFor( m => m.Post.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--- 选择类别 ---")
}

3- 另一个问题是无效的ModelState。正如您之前看到的,类别不是可为空的变量。当我尝试插入帖子数据时,类别为null。我不知道为什么会发生这种情况,但我再次使用了categoryId号添加了一个查询。这些语句是注释行,因为我认为两次查询对我来说没有意义。因此,我将类别变量更改为可为空。

public async Task<IActionResult> OnPostAsync()
{
 //Category category = _context.Category.First(m => m.Id == Post.CategoryId);
 //Post.Category = category;
 if (!ModelState.IsValid || _context.Post == null || Post == null) { 
    return Page();
 }
 _context.Post.Add(Post);
 await _context.SaveChangesAsync();
 return RedirectToPage("./Index");
 }

4- 最后,这一步不直接与主题相关,但我想与您分享。为了在帖子索引页面上呈现所有类别名称,我使用了急加载搜索。我在下面分享了Index页面的OnGetAysnc方法:

public async Task OnGetAsync()
{
  if (_context.Post != null)
  {
    Post = await _context.Post.Include(m => m.Category).ToListAsync();
  }
}

并在index HTML表格中添加了一些行,如下所示:

<td>
  @Html.DisplayFor(modelItem => item.Category.Name)
</td>
英文:

I solved this issue in a few steps:

1- CategoryId variable is added to Post Class. Without any changing on post table, EF performed this. This is my updated Post Class:

public class Post
{
    public int Id { get; set; }
    // other variables
    public int CategoryId { get; set; } // This variable was added later.
    public Category? Category { get; set; }
}

And then, I changed m.Post.Category.Id HTML helper LINQ statements to m.CategoryId

2- OnGetAsync method change to OnGet(). Although rendering all categories on the web page, Async method caused to null Category Model. this was determined with if the condition on create.cshtml

@if (Model.Categories != null)
{
  @Html.DropDownListFor( m => m.Post.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--- Select Category ---")
}

3- Another issue is the invalid ModelState. As you see before, the category is not a nullable variable. When I try to insert post data, the category is null. I don't know why this happens but I added a query again using categoryId number. Those statements are comment lines because twice query makes unsense to me. So, I change to category variable to nullable.

public async Task<IActionResult> OnPostAsync()
{
 //Category category = _context.Category.First(m => m.Id == Post.CategoryId);
 //Post.Category = category;
 if (!ModelState.IsValid || _context.Post == null || Post == null) { 
    return Page();
 }
 _context.Post.Add(Post);
 await _context.SaveChangesAsync();
 return RedirectToPage("./Index");
 }

4- Finally, this step not directly about topic but I want to share with you. To render all category names on the Post Index Page, I used eager load search. I shared Index Page's OnGetAysnc method below:

public async Task OnGetAsync()
{
  if (_context.Post != null)
  {
    Post = await _context.Post.Include(m => m.Category).ToListAsync();
  }
}

And add some lines to index html table like below:

<td>
  @Html.DisplayFor(modelItem => item.Category.Name)
</td>

huangapple
  • 本文由 发表于 2023年6月22日 13:22:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528797.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定