Entity Framework 核心不保存更新
Entity Framework Core not saving updates
我有一个绘画网络应用程序,它使用 ASP.NET Core、Angular、EF Core、SQL 服务器、带有存储库模式的 AutoMapper。
问题是,当我尝试从绘画 table 更新单个绘画时,它没有保存到数据库中。我用同样的方法尝试了其他 tables 以查看是否是流程问题,但它们成功保存。
通过 swagger 我调用了 put 方法,这调用了绘画控制器,进入存储库,存储库 returns 更新的对象但是当我进入数据库时没有任何更新。如果我大摇大摆地调用 get 操作,我也看不到更新。
当我添加断点以查看数据时,从头到尾一切看起来都很好,但它只是没有保存到数据库中。为了测试,我什至尝试删除自动映射器逻辑并在更新方法中手动创建一个对象,并将现有对象属性设置为这些硬编码值以查看它是传入数据,但仍然没有成功。同样,为了测试,我尝试更新其他 tables 并且那些有效。
控制器
[HttpPut("{paintingId:int}")]
public async Task<IActionResult> UpdatePaintingAsync(int paintingId, [FromBody] UpdatePaintingRequest updatePaintingRequest)
{
try
{
if (await repository.Exists(paintingId))
{
var updatedPaiting = await repository.UpdatePainting(paintingId, mapper.Map<DataModels.Painting>(updatePaintingRequest));
if (updatedPaiting != null)
{
return Ok(updatePaintingRequest);
}
}
return NotFound();
}
catch (Exception ex)
{
logger.LogError($"Failed to update painting: {ex}");
return BadRequest("Failed to update painting");
}
}
从存储库更新方法
public async Task<Painting> UpdatePainting(int paintingId, Painting request)
{
var existingPainting = await GetPaintingByIdAsync(paintingId);
if (existingPainting != null)
{
existingPainting.Name = request.Name;
existingPainting.Description = request.Description;
existingPainting.ImageUrl = request.ImageUrl;
existingPainting.IsOriginalAvailable = request.IsOriginalAvailable;
existingPainting.IsPrintAvailable = request.IsPrintAvailable;
existingPainting.IsActive = request.IsActive;
await context.SaveChangesAsync();
return existingPainting;
}
return null;
}
获取绘画进行更新
public async Task<Painting> GetPaintingByIdAsync(int paintingId)
{
return await context.Painting
.Include(x => x.PaintingCategories)
.ThenInclude(c => c.Category)
.AsNoTracking()
.Where(x => x.PaintingId == paintingId)
.FirstOrDefaultAsync();
}
模型(在 DAO 和 DTO 上完全相同)
public class Painting
{
public int PaintingId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public bool IsOriginalAvailable { get; set; }
public bool IsPrintAvailable { get; set; }
public bool IsActive { get; set; }
public ICollection<PaintingCategory> PaintingCategories { get; set; }
}
上下文
public class JonathanKrownContext : DbContext
{
public JonathanKrownContext(DbContextOptions<JonathanKrownContext> options) : base(options)
{
}
public DbSet<Painting> Painting { get; set; }
}
ModelBuilder.Entity
modelBuilder.Entity("JonathanKrownArt.API.DataModels.Painting", b =>
{
b.Property<int>("PaintingId")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsOriginalAvailable")
.HasColumnType("bit");
b.Property<bool>("IsPrintAvailable")
.HasColumnType("bit");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("PaintingId");
b.ToTable("Painting");
});
您的问题是您在获取实体时使用了 AsNoTracking,因此上下文不再跟踪更改。因此,您需要在保存之前附加它或删除 AsNoTracking。
如果您不想附加实体,您需要将 GetPaintingByIdAsync 更改为:
public async Task<Painting> GetPaintingByIdAsync(int paintingId)
{
return await context.Painting
.Include(x => x.PaintingCategories)
.ThenInclude(c => c.Category)
.Where(x => x.PaintingId == paintingId)
.FirstOrDefaultAsync();
}
如果您想保留 AsNoTracking,那么您需要在 UpdatePainting 中添加:
context.Painting.Update(existingPainting);
在调用保存之前。
更新方法执行以下操作:
Begins tracking the given entity in the Modified state such that it
will be updated in the database when SaveChanges() is called.
所以把你的方法改成这样:
public async Task<Painting> UpdatePainting(int paintingId, Painting request)
{
var existingPainting = await GetPaintingByIdAsync(paintingId);
if (existingPainting != null)
{
existingPainting.Name = request.Name;
existingPainting.Description = request.Description;
existingPainting.ImageUrl = request.ImageUrl;
existingPainting.IsOriginalAvailable = request.IsOriginalAvailable;
existingPainting.IsPrintAvailable = request.IsPrintAvailable;
existingPainting.IsActive = request.IsActive;
context.Painting.Update(existingPainting);
await context.SaveChangesAsync();
return existingPainting;
}
return null;
}
我认为使用 AsNoTracking()
是一个很好的做法,您应该尽可能使用它,但是在 Update
的情况下,您需要通过此 EF
将实体附加到上下文知道这个实体应该更新。
因此,为了解决您的问题,只需在代码中添加一行,如下所示:
//other lines
context.Attach(existingPainting); //<--- by this line you tell EF to track the entity
context.Painting.Update(existingPainting);
await context.SaveChangesAsync();
我有一个绘画网络应用程序,它使用 ASP.NET Core、Angular、EF Core、SQL 服务器、带有存储库模式的 AutoMapper。
问题是,当我尝试从绘画 table 更新单个绘画时,它没有保存到数据库中。我用同样的方法尝试了其他 tables 以查看是否是流程问题,但它们成功保存。
通过 swagger 我调用了 put 方法,这调用了绘画控制器,进入存储库,存储库 returns 更新的对象但是当我进入数据库时没有任何更新。如果我大摇大摆地调用 get 操作,我也看不到更新。
当我添加断点以查看数据时,从头到尾一切看起来都很好,但它只是没有保存到数据库中。为了测试,我什至尝试删除自动映射器逻辑并在更新方法中手动创建一个对象,并将现有对象属性设置为这些硬编码值以查看它是传入数据,但仍然没有成功。同样,为了测试,我尝试更新其他 tables 并且那些有效。
控制器
[HttpPut("{paintingId:int}")]
public async Task<IActionResult> UpdatePaintingAsync(int paintingId, [FromBody] UpdatePaintingRequest updatePaintingRequest)
{
try
{
if (await repository.Exists(paintingId))
{
var updatedPaiting = await repository.UpdatePainting(paintingId, mapper.Map<DataModels.Painting>(updatePaintingRequest));
if (updatedPaiting != null)
{
return Ok(updatePaintingRequest);
}
}
return NotFound();
}
catch (Exception ex)
{
logger.LogError($"Failed to update painting: {ex}");
return BadRequest("Failed to update painting");
}
}
从存储库更新方法
public async Task<Painting> UpdatePainting(int paintingId, Painting request)
{
var existingPainting = await GetPaintingByIdAsync(paintingId);
if (existingPainting != null)
{
existingPainting.Name = request.Name;
existingPainting.Description = request.Description;
existingPainting.ImageUrl = request.ImageUrl;
existingPainting.IsOriginalAvailable = request.IsOriginalAvailable;
existingPainting.IsPrintAvailable = request.IsPrintAvailable;
existingPainting.IsActive = request.IsActive;
await context.SaveChangesAsync();
return existingPainting;
}
return null;
}
获取绘画进行更新
public async Task<Painting> GetPaintingByIdAsync(int paintingId)
{
return await context.Painting
.Include(x => x.PaintingCategories)
.ThenInclude(c => c.Category)
.AsNoTracking()
.Where(x => x.PaintingId == paintingId)
.FirstOrDefaultAsync();
}
模型(在 DAO 和 DTO 上完全相同)
public class Painting
{
public int PaintingId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public bool IsOriginalAvailable { get; set; }
public bool IsPrintAvailable { get; set; }
public bool IsActive { get; set; }
public ICollection<PaintingCategory> PaintingCategories { get; set; }
}
上下文
public class JonathanKrownContext : DbContext
{
public JonathanKrownContext(DbContextOptions<JonathanKrownContext> options) : base(options)
{
}
public DbSet<Painting> Painting { get; set; }
}
ModelBuilder.Entity
modelBuilder.Entity("JonathanKrownArt.API.DataModels.Painting", b =>
{
b.Property<int>("PaintingId")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("Description")
.HasColumnType("nvarchar(max)");
b.Property<string>("ImageUrl")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsOriginalAvailable")
.HasColumnType("bit");
b.Property<bool>("IsPrintAvailable")
.HasColumnType("bit");
b.Property<string>("Name")
.HasColumnType("nvarchar(max)");
b.HasKey("PaintingId");
b.ToTable("Painting");
});
您的问题是您在获取实体时使用了 AsNoTracking,因此上下文不再跟踪更改。因此,您需要在保存之前附加它或删除 AsNoTracking。
如果您不想附加实体,您需要将 GetPaintingByIdAsync 更改为:
public async Task<Painting> GetPaintingByIdAsync(int paintingId)
{
return await context.Painting
.Include(x => x.PaintingCategories)
.ThenInclude(c => c.Category)
.Where(x => x.PaintingId == paintingId)
.FirstOrDefaultAsync();
}
如果您想保留 AsNoTracking,那么您需要在 UpdatePainting 中添加:
context.Painting.Update(existingPainting);
在调用保存之前。
更新方法执行以下操作:
Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges() is called.
所以把你的方法改成这样:
public async Task<Painting> UpdatePainting(int paintingId, Painting request)
{
var existingPainting = await GetPaintingByIdAsync(paintingId);
if (existingPainting != null)
{
existingPainting.Name = request.Name;
existingPainting.Description = request.Description;
existingPainting.ImageUrl = request.ImageUrl;
existingPainting.IsOriginalAvailable = request.IsOriginalAvailable;
existingPainting.IsPrintAvailable = request.IsPrintAvailable;
existingPainting.IsActive = request.IsActive;
context.Painting.Update(existingPainting);
await context.SaveChangesAsync();
return existingPainting;
}
return null;
}
我认为使用 AsNoTracking()
是一个很好的做法,您应该尽可能使用它,但是在 Update
的情况下,您需要通过此 EF
将实体附加到上下文知道这个实体应该更新。
因此,为了解决您的问题,只需在代码中添加一行,如下所示:
//other lines
context.Attach(existingPainting); //<--- by this line you tell EF to track the entity
context.Painting.Update(existingPainting);
await context.SaveChangesAsync();