更新数据库条目时尝试跟踪实体实例时出错
Error when trying to track instance of an entity while updating the database entries
我想修改我的数据库的一列table(通知table)。相关的通知 Id 由视图确定,并使用 Ajax 调用发送回控制器。我的控制器代码:
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
string notificationText = _notificationRepository.GetNotificationById(notificationId).isSeen;
var myNotification = new Notification()
{
isSeen = string.Format("{0},{1}", notificationText, userId)
};
bool result = _notificationRepository.UpdateNotification(notificationId, myNotification);
if (result == true)
{
DeleteNotification(notificationId);
return Json(new { success = true});
}
else
{
return Json(new { success = false });
}
}
行 bool result = _notificationRepository.UpdateNotification(notificationId, myNotification);
链接到存储库实现:
bool INotificationRepository.UpdateNotification(int selectedNotId, Notification notification)
{
try
{
var selectedNotification = new Notification()
{
notificationId = selectedNotId,
isSeen = notification.isSeen
};
context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
return true;
}
catch (Exception)
{
return false;
}
}
当我调试时,在行 context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
中出现以下错误并说:
System.InvalidOperationException: 'The instance of entity type
'Notification' cannot be tracked because another instance with the
same key value for {'notificationId'} is already being tracked. When
attaching existing entities, ensure that only one entity instance with
a given key value is attached. Consider using
'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the
conflicting key values.'
我尝试使用 context.Entry(selectedNotId).State = Microsoft.EntityFrameworkCore.EntityState.Detached
将 notificationId
从实体中分离出来,但代码停在这一行,没有任何反应。我该如何解决这个问题?
更新:
我使用相同的 DbContext 从数据库中读取 notificationId table(在视图中)。
我在 Program.cs 中添加了范围存储库:
builder.Services.AddScoped<INotificationRepository, NotificationRepository>();
你为什么不使用更简单的方法?什么时候可以重用存储库中的实体?示例如下:
控制器代码
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.isSeen = string.Format("{0},{1}", notificationText, userId);
bool result = _notificationRepository.UpdateNotification(notification);
if (result == true)
{
DeleteNotification(notificationId);
return Json(new { success = true});
}
else
{
return Json(new { success = false });
}
}
存储库实现
bool INotificationRepository.UpdateNotification(Notification notification)
{
try
{
context.Update(notification);
}
catch (Exception)
{
return false;
}
}
在实现像存储库这样的东西时,这是一个常见的问题,您在其中传递实体引用并且没有明确定义 DbContext 的范围。
问题是您对实体的重新定义太多太多了。采用您的控制器方法:
string notificationText = _notificationRepository.GetNotificationById(notificationId).isSeen;
这是告诉存储库加载整个通知,然后检索它的文本 属性。有可能实例正在被 DbContext 跟踪,并且 DbContext 的范围是请求的生命周期。当您调用 Update 方法时,仍会跟踪由 GetNotificationById() 加载的实例,但您正在创建实体的新实例以传递给您的更新方法,然后在您的更新方法中创建实体的另一个实例,都具有相同的 ID。
这完全没有必要。我也想不通你为什么要费心去更新通知,然后调用一个方法来删除通知??
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
try
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.IsSeen = string.Format("{0},{1}", notificationText, userId);
_context.SaveChanges();
return Json(new { success = true});
}
catch(Exception)
{
return Json(new { success = false});
}
}
由于存储库加载了一个实体,我们可以对该实体使用更改跟踪,然后使用作用域 DbContext 来保存更改。这确实违背了在 DbContext 之上添加存储库层的目的,因为您可以使用 DbContext 本身获取实体。通常使用工作单元模式你会得到更像的东西:
using (var scope = _unitOfWork.CreateScope())
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.IsSeen = string.Format("{0},{1}", notificationText, userId);
scope.SaveChanges();
}
存储库可以从调用它们的 UoW 范围解析 DbContext。 (即使用定位器或将范围传递到每个方法调用中)
您要避免的主要事情是不断更新同一行的实体 类。 (Id) 获取一个实体,利用 EF 的内置更改跟踪更新它的值,然后调用 SaveChanges()。如果您曾经遇到过要更新分离实体的情况,请首先检查 DbContext 以查看它是否在附加之前跟踪实体。例如,如果您修改传递到存储库的分离通知实体中的详细信息,并希望在 entity/properties:
上使用更新或附加并设置修改后的状态
bool INotificationRepository.UpdateNotification(Notification notification)
{
try
{
var existingNotification = context.Notifications.Local.SingleOrdefault(x => x.notificationId == notification.notificationId);
if (existingNotification != null)
{
existingNotification.isSeen = notification.isSeen;
}
else
{
context.Attach(notification);
context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
}
return true;
}
catch (Exception)
{
return false;
}
}
我想修改我的数据库的一列table(通知table)。相关的通知 Id 由视图确定,并使用 Ajax 调用发送回控制器。我的控制器代码:
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
string notificationText = _notificationRepository.GetNotificationById(notificationId).isSeen;
var myNotification = new Notification()
{
isSeen = string.Format("{0},{1}", notificationText, userId)
};
bool result = _notificationRepository.UpdateNotification(notificationId, myNotification);
if (result == true)
{
DeleteNotification(notificationId);
return Json(new { success = true});
}
else
{
return Json(new { success = false });
}
}
行 bool result = _notificationRepository.UpdateNotification(notificationId, myNotification);
链接到存储库实现:
bool INotificationRepository.UpdateNotification(int selectedNotId, Notification notification)
{
try
{
var selectedNotification = new Notification()
{
notificationId = selectedNotId,
isSeen = notification.isSeen
};
context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
return true;
}
catch (Exception)
{
return false;
}
}
当我调试时,在行 context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
中出现以下错误并说:
System.InvalidOperationException: 'The instance of entity type 'Notification' cannot be tracked because another instance with the same key value for {'notificationId'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
我尝试使用 context.Entry(selectedNotId).State = Microsoft.EntityFrameworkCore.EntityState.Detached
将 notificationId
从实体中分离出来,但代码停在这一行,没有任何反应。我该如何解决这个问题?
更新: 我使用相同的 DbContext 从数据库中读取 notificationId table(在视图中)。
我在 Program.cs 中添加了范围存储库:
builder.Services.AddScoped<INotificationRepository, NotificationRepository>();
你为什么不使用更简单的方法?什么时候可以重用存储库中的实体?示例如下:
控制器代码
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.isSeen = string.Format("{0},{1}", notificationText, userId);
bool result = _notificationRepository.UpdateNotification(notification);
if (result == true)
{
DeleteNotification(notificationId);
return Json(new { success = true});
}
else
{
return Json(new { success = false });
}
}
存储库实现
bool INotificationRepository.UpdateNotification(Notification notification)
{
try
{
context.Update(notification);
}
catch (Exception)
{
return false;
}
}
在实现像存储库这样的东西时,这是一个常见的问题,您在其中传递实体引用并且没有明确定义 DbContext 的范围。
问题是您对实体的重新定义太多太多了。采用您的控制器方法:
string notificationText = _notificationRepository.GetNotificationById(notificationId).isSeen;
这是告诉存储库加载整个通知,然后检索它的文本 属性。有可能实例正在被 DbContext 跟踪,并且 DbContext 的范围是请求的生命周期。当您调用 Update 方法时,仍会跟踪由 GetNotificationById() 加载的实例,但您正在创建实体的新实例以传递给您的更新方法,然后在您的更新方法中创建实体的另一个实例,都具有相同的 ID。
这完全没有必要。我也想不通你为什么要费心去更新通知,然后调用一个方法来删除通知??
[HttpPost]
public JsonResult UpdateNotification(int notificationId, int userId)
{
try
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.IsSeen = string.Format("{0},{1}", notificationText, userId);
_context.SaveChanges();
return Json(new { success = true});
}
catch(Exception)
{
return Json(new { success = false});
}
}
由于存储库加载了一个实体,我们可以对该实体使用更改跟踪,然后使用作用域 DbContext 来保存更改。这确实违背了在 DbContext 之上添加存储库层的目的,因为您可以使用 DbContext 本身获取实体。通常使用工作单元模式你会得到更像的东西:
using (var scope = _unitOfWork.CreateScope())
{
var notification = _notificationRepository.GetNotificationById(notificationId);
notification.IsSeen = string.Format("{0},{1}", notificationText, userId);
scope.SaveChanges();
}
存储库可以从调用它们的 UoW 范围解析 DbContext。 (即使用定位器或将范围传递到每个方法调用中)
您要避免的主要事情是不断更新同一行的实体 类。 (Id) 获取一个实体,利用 EF 的内置更改跟踪更新它的值,然后调用 SaveChanges()。如果您曾经遇到过要更新分离实体的情况,请首先检查 DbContext 以查看它是否在附加之前跟踪实体。例如,如果您修改传递到存储库的分离通知实体中的详细信息,并希望在 entity/properties:
上使用更新或附加并设置修改后的状态bool INotificationRepository.UpdateNotification(Notification notification)
{
try
{
var existingNotification = context.Notifications.Local.SingleOrdefault(x => x.notificationId == notification.notificationId);
if (existingNotification != null)
{
existingNotification.isSeen = notification.isSeen;
}
else
{
context.Attach(notification);
context.Entry(selectedNotification).Property(x => x.isSeen).IsModified = true;
}
return true;
}
catch (Exception)
{
return false;
}
}