我正在 ASP.NET Core 中开发一个管理系统项目,在将相关实体保存到数据库时遇到了问题。我将使用一个简化的示例来描述该问题。我有两个
我正在 ASP.NET Core 中开发一个管理系统项目,在将相关实体保存到数据库时遇到了问题。我将使用一个简化的示例来描述该问题。以下屏幕截图中有两个可见的类。我还有一个端点,它应该创建一个新的 Car 实例并将其添加到 User 类中的 Cars 集合中,然后将更改保存到数据库。但是,当我调用此端点时,出现以下异常:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException:数据库操作预计会影响 1 行,但实际影响了 0 行;自加载实体以来数据可能已被修改或删除。
我查看了微软文档,似乎我的代码与示例没有什么不同,所以如果有人能解释问题出在哪里,我将不胜感激。
模型:
public class User : BaseEntity
{
public string FirstName { get; protected set; }
public string LastName { get; protected set; }
public ICollection<Car> Cars { get; set; }
public User(){}
public User(Guid id, string firstName, string lastName)
{
Id = id;
FirstName = firstName;
LastName = lastName;
}
public void AddCar(Car car)
{
Cars.Add(car);
}
}
public class Car : BaseEntity
{
public string Model { get; protected set; }
public DateOnly Year { get; protected set; }
public Guid UserId { get; set; }
public User User { get; protected set; }
public Car() {}
public Car(Guid id, string model, DateOnly year)
{
Id = id;
Model = model;
Year = year;
}
}
语境:
public class AppDbContext : DbContext
{
public AppDbContext() { }
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<User> Users { get; set; }
public DbSet<Car> Cars { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("test");
modelBuilder.Entity<User>(entity =>
{
entity.HasKey(x => x.Id);
entity.HasMany(u => u.Cars)
.WithOne(c => c.User)
.HasForeignKey(c => c.UserId);
});
modelBuilder.Entity<Car>(entity =>
{
entity.HasKey(c => c.Id);
entity.HasOne(c => c.User)
.WithMany(u => u.Cars)
.HasForeignKey(c => c.UserId);
});
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
端点:
[HttpPost("{userId}/add-car")]
public async Task<IActionResult> AddCar(Guid userId, [FromBody] AddCarRequest request)
{
await using (var context = new AppDbContext())
{
var car = new Car(Guid.NewGuid(), request.Model, request.Year);
var user = context.Users.Include(x => x.Cars).FirstOrDefault(x => x.Id == userId);
user.AddCar(car);
await context.SaveChangesAsync();
return Ok();
}
}
例外:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader, Boolean async, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
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(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at EFTest.Controllers.DefaultController.AddCar(Guid userId, AddCarRequest request) in S:\Repos\EFTest\EFTest\Controllers\DefaultController.cs:line 38
at EFTest.Controllers.DefaultController.AddCar(Guid userId, AddCarRequest request) in S:\Repos\EFTest\EFTest\Controllers\DefaultController.cs:line 40
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
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.<InvokeFilterPipelineAsync>g__Awaited|20_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.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
我将我的代码与微软的文档进行了比较,在我看来,一切似乎都是正确的。
using (var context = new BloggingContext())
{
var blog = context.Blogs.Include(b => b.Posts).First();
var post = new Post { Title = "Intro to EF Core" };
blog.Posts.Add(post);
context.SaveChanges();
}
EF 执行的查询:
info: 13.09.2024 20:51:02.616 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (20ms) [Parameters=[@__userId_0='42d2ac6e-e663-41a9-830e-3e49b38634c9'], CommandType='Text', CommandTimeout='30']
SELECT t."Id", t."CreatedAt", t."FirstName", t."LastName", t."UpdatedAt", c."Id", c."CreatedAt", c."Model", c."UpdatedAt", c."UserId", c."Year"
FROM (
SELECT u."Id", u."CreatedAt", u."FirstName", u."LastName", u."UpdatedAt"
FROM test."Users" AS u
WHERE u."Id" = @__userId_0
LIMIT 1
) AS t
LEFT JOIN test."Cars" AS c ON t."Id" = c."UserId"
ORDER BY t."Id"
info: 13.09.2024 20:51:03.025 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (23ms) [Parameters=[@p5='7c87ad78-534b-4e1c-a472-653eab8a0fb9', @p0='0001-01-01T00:00:00.0000000' (DbType = DateTime), @p1='TEST' (Nullable = false), @p2='0001-01-01T00:00:00.0000000' (DbType = DateTime), @p3='42d2ac6e-e663-41a9-830e-3e49b38634c9', @p4='01/01/2000' (DbType = Date)], CommandType='Text', CommandTimeout='30']
UPDATE test."Cars" SET "CreatedAt" = @p0, "Model" = @p1, "UpdatedAt" = @p2, "UserId" = @p3, "Year" = @p4
WHERE "Id" = @p5;