【问题标题】:Creating a new comment only works once创建新评论只能使用一次
【发布时间】:2021-09-07 18:36:04
【问题描述】:

我创建了一种在帖子上发布 cmets 的方法。当我第一次发表评论时,一切正常。它出现在帖子下方,保存在数据库等中。但是,如果我想立即创建第二条评论(不刷新页面-因为它确实有效)我收到此错误:

Microsoft.EntityFrameworkCore.Update: Error: An exception occurred in the database while saving changes for context type 'JoyAndFaithLicenta.Data.DataContext'.
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
 ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert explicit value for identity column in table 'AspNetUsers' when IDENTITY_INSERT is set to OFF.
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
   at System.Threading.Tasks.Task.<>c.<.cctor>b__274_0(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)

at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync
(IRelationalConnection connection, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at 
Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at JoyAndFaithLicenta.Data.UserRepository.SaveAllAsync() in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Data\UserRepository.cs:line 84 at JoyAndFaithLicenta.Helpers.LogUserActivity.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Helpers\LogUserActivity.cs:line 30 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at 
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at 
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at 
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at JoyAndFaithLicenta.Middleware.ExceptionMiddleware.InvokeAsync(HttpContext context) in C:\Users\Georgia\source\repos\JoyAndFaithLicenta\JoyAndFaithLicenta\Middleware\ExceptionMiddleware.cs:line 33

这是为什么呢?这是我遇到此问题的唯一功能。但是,所有其他功能都有一个在存储库中创建的 SaveAsync() 方法,而我还没有为 cmets 创建存储库,所以我使用的是 DataContext:_context.SaveChangesAsync()

这是发表评论的方法:

[HttpPost("create/{postId}")]
public async Task<ActionResult<CommentDto>> PostComment(CommentDto commentDto, int postId)
{
    if (!ModelState.IsValid) return BadRequest("Not a valid model");
    var user = await _userRepository.GetUserByUsernameAsync(User.GetUsername());

    var comment = new Comment
    {
        PostId = postId,
        UserId = user.Id,
        Text = commentDto.Text,
        Created = commentDto.Created,
        User = commentDto.User
    };

    _context.Comments.Add(comment);

    await _context.SaveChangesAsync();

    return new CommentDto
    {
        PostId = comment.PostId,
        UserId = comment.UserId,
        Text = comment.Text,
        Created = comment.Created,
        User = comment.User
    };
}

这是评论实体:

public class Comment
{
    public int Id { get; set; }
    public DateTime Created { get; set; } = DateTime.Now;
    public string Text { get; set; }

    [ForeignKey("UserId")]
    public User User { get; set; }
    public int UserId { get; set; }

    [ForeignKey("PostId")]
    public Post Post { get; set; }
    public int? PostId { get; set; }
}

【问题讨论】:

  • 您忘记发布错误。您只发布了调用堆栈。是否违反约束?并发冲突?还有什么? Comment的ID是怎么产生的?自动生成的 IDENTITY 不需要任何类型的刷新。一个 MAX+1 会(并且还会产生大量其他问题)
  • 我不知道。这就是我在调试时收到的全部内容。如果它有任何用处,用户实体包含一个 cmets 列表,而 post 实体也包含一个 cmets 列表。它在这条线上崩溃:_context.Comments.Add(comment);
  • 评论的id是一个随机数,但通常第一个id为1,其他的每次加1。我对此没有额外的逻辑。此外,如果我创建评论,请将其删除,然后再创建一个新评论,我会收到同样的错误。
  • 发布完整的异常文本。您没有在标题或问题中提及任何错误
  • The comment's id is a random number, but usually the first id is 1 and the others will be incremented by 1 each time. I have no extra logic for this. 保证会导致重复 ID。为什么不使用 IDENTITY ?生成此 ID 的实际代码是什么? Random 不会返回 1,也不会增加值。你有额外的逻辑这样做,一段时间后总是会失败

标签: c# asp.net sql-server asp.net-core


【解决方案1】:

我看不到你的 onModelCreating 函数,但我认为这是因为你的评论 ID 中没有 [Key]

public class Comment
{
    [Key]
    public int Id { get; set; }
    public DateTime Created { get; set; } = DateTime.Now;
    public string Text { get; set; }

    [ForeignKey("UserId")]
    public User User { get; set; }
    public int UserId { get; set; }

    [ForeignKey("PostId")]
    public Post Post { get; set; }
    public int? PostId { get; set; }
}

然后数据库将使用Id作为Key,并且您确定key不会重复。

另外,我认为你被注入了DataContext 服务错误。 尝试像这样做同样的工作:

using(var cnx = new DataContext()
{
var comment = new Comment
    {
        PostId = postId,
        UserId = user.Id,
        Text = commentDto.Text,
        Created = commentDto.Created,
        User = commentDto.User
    };

    cnx.Comments.Add(comment);

    await cnx.SaveChangesAsync();
}

如果可行,则说明 Startup.cs 中的 DI 存在问题。

记住,注入EntityFramework的DataContext的最好方法是:

services.AddDbContext<DataContext>();

【讨论】:

  • 我试过这个,但它不起作用。这是模型构建器b.Property&lt;int&gt;("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .UseIdentityColumn();。我在 onModelCreating 中没有关于 cmets 的任何信息。
  • 这是我的 onModelCreating:protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder.Entity&lt;User&gt;().HasMany(ur =&gt; ur.UserRoles) .WithOne(u =&gt; u.User).HasForeignKey(ui =&gt; ui.UserId).IsRequired(); builder.Entity&lt;Role&gt;().HasMany(ur =&gt; ur.UserRoles) .WithOne(r =&gt; r.Role).HasForeignKey(ri =&gt; ri.RoleId).IsRequired(); }
  • 我的回答做了一些改动。
  • 这正是我注入它的方式。它也适用于其他功能。我认为可能只是 cmets 有问题?
  • 已解决,我在创建评论时添加了用户,但我只需要UserId。这就是导致错误的原因。
【解决方案2】:

根据下面的代码

 var comment = new Comment
{
    PostId = postId,
    UserId = user.Id,
    Text = commentDto.Text,
    Created = commentDto.Created,
    User = commentDto.User
};

您也在尝试插入新用户。由于您正在尝试创建具有分配 ID 的新用户,因此您会收到此错误。因为 Aspnetuser 表不允许显式传递 ID,因为 IDENTITY_INSERT 默认设置为 false。

我不确定您为什么还要插入用户以及评论。可能这可能是一个要求。如果你真的想创建一个用户,那么你的 User 对象不应该有一个指定的 ID 值。

如果这是错误的,请将 User 更改为 UserId。

【讨论】:

  • 是的,我已经通过删除新用户的那一行解决了这个问题。我在另一个答案上对此发表了评论。不过,非常感谢您的回答!
猜你喜欢
  • 2020-01-09
  • 1970-01-01
  • 1970-01-01
  • 2012-02-26
  • 2014-12-21
  • 1970-01-01
  • 2021-11-01
  • 1970-01-01
  • 2013-02-18
相关资源
最近更新 更多