Ale*_*ski 3 c# asp.net-mvc exception entity-framework-core .net-core
我有一个与类别有关系的书签,因此每次更新现有书签时,都应该创建或更新一个新类别(如果它已经存在)。当我更新现有书签时,出现此错误:
无法跟踪实体类型“Bookmark”的实例,因为已跟踪具有相同键值 {'ID'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。考虑使用“DbContextOptionsBuilder.EnableSensitiveDataLogging”来查看冲突的键值。
我的Entity看起来像这样:
public class Bookmark
{
[Key]
public int ID { get; set; }
[StringLength(maximumLength: 500)]
public string URL { get; set; }
public string ShortDescription { get; set; }
public int? CategoryId { get; set; }
public virtual Category Category { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}")]
public DateTime CreateDate { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我的Update控制器看起来像这样:
public async Task<IActionResult> Edit(BookmarkViewModel request)
{
var bookmark = _mapper.Map<Bookmark>(request);
var exists = await _bookmarkService.GetBookmark(bookmark.ID);
if(exists == null)
return new StatusCodeResult(Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound);
if (ModelState.IsValid)
{
if (request.CategoryName != null)
{
var category = _categoryService.CreateCategory(new Category
{
Name = request.CategoryName
});
bookmark.Category = category;
bookmark.CategoryId = category.ID;
}
await _bookmarkService.UpdateBookmark(bookmark);
return RedirectToAction("Index");
}
return View(bookmark);
}
Run Code Online (Sandbox Code Playgroud)
我的CreateCategory服务如下所示:
public Category CreateCategory(Category category)
{
category.UserId = _identityService.GetUserId();
var existing = _ReadLaterDataContext.Categories
.FirstOrDefault(x => x.Name.Equals(category.Name) && x.UserId.Equals(category.UserId));
if (existing != null)
return existing;
_ReadLaterDataContext.Add(category);
_ReadLaterDataContext.SaveChanges();
return category;
}
Run Code Online (Sandbox Code Playgroud)
我的UpdateBookmark服务如下所示:
public async Task UpdateBookmark(Bookmark bookmark)
{
bookmark.CreateDate = DateTime.Now;
UpdateExistingCategory(bookmark);
_ReadLaterDataContext.Update(bookmark);
await _ReadLaterDataContext.SaveChangesAsync();
}
Run Code Online (Sandbox Code Playgroud)
我的GetBookmark()方法
public async Task<Bookmark> GetBookmark(int Id)
{
return await _ReadLaterDataContext.Bookmark.Where(x => x.ID == Id && x.UserId.Equals(_identityService.GetUserId())).Include(x => x.Category).FirstOrDefaultAsync();
}
Run Code Online (Sandbox Code Playgroud)
问题就在这里:
await _bookmarkService.UpdateBookmark(bookmark);
Run Code Online (Sandbox Code Playgroud)
这会导致身份解析违规。以下是链接的摘录(带有强调):
DbContext 只能跟踪具有任何给定主键值的一个实体实例。这意味着具有相同键值的实体的多个实例必须解析为单个实例。
Bookmark执行此语句后,更改跟踪器立即开始跟踪:
var exists = await _bookmarkService.GetBookmark(bookmark.ID);
Run Code Online (Sandbox Code Playgroud)
更改跟踪是 EF Core 的一项功能,其中从数据库获取实体后,将跟踪对实体所做的任何更改。所有这些更改将在您致电后立即应用context.SaveChanges()。
Bookmark现在,如果 EF Core 发现除具有相同主键的实例之外的任何其他实例exists,它会引发错误。在你的情况下:
var bookmark = _mapper.Map<Bookmark>(request);
Run Code Online (Sandbox Code Playgroud)
在上面的语句中,是具有相同主键 ( )bookmark的另一个实例。该 ID 是在模型绑定期间通过发布请求设置的。因此,为了解决这个问题,您必须使用以下方法禁用更改跟踪:BookmarkBookmark.IDAsNoTracking()
public async Task<Bookmark> GetBookmark(int Id)
=> await _ReadLaterDataContext.Bookmark
.AsNoTracking()
.Where(x => x.ID == Id
&& x.UserId.Equals(_identityService.GetUserId()))
.Include(x => x.Category)
.FirstOrDefaultAsync();
Run Code Online (Sandbox Code Playgroud)
或者,更好的选择是在从数据库中获取的同一实例中进行更改(在您的情况下为exists)。您还将新创建的类别分配category给bookmark.Category而不检查是否Bookmark已包含另一个类别。检查书签是否已包含类别,如果存在则更改该类别的名称,如果不存在则创建一个新类别。
看来您不需要映射的书签,所以我已经完全摆脱了它并重命名exists为bookmark. 我假设request包含一个 property ID。使用包含书签 ID 的属性:
public async Task<IActionResult> Edit(BookmarkViewModel request)
{
var bookmark = await _bookmarkService.GetBookmark(request.ID);
if (bookmark == null)
return new StatusCodeResult(Microsoft.AspNetCore.Http.StatusCodes.Status404NotFound);
if (ModelState.IsValid)
{
if (request.CategoryName is not null)
{
if (bookmark.Category is not null)
{
bookmark.Category.Name = request.CategoryName;
}
else
{
var category = _categoryService.CreateCategory(new Category
{
Name = request.CategoryName
});
bookmark.Category = category;
bookmark.CategoryId = category.ID;
}
}
await _bookmarkService.UpdateBookmark(bookmark);
return RedirectToAction("Index");
}
return View(request);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
14809 次 |
| 最近记录: |