我尝试更新实体,但收到此常见异常:
存储更新,插入或删除语句影响了意外 行数(0)。自此以来,实体可能已被修改或删除 实体已加载。看到 http://go.microsoft.com/fwlink/?linkid=472540,以获取有关的信息 了解和处理乐观并发异常。
我有此代码:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Atualizar(TipoDocumentoViewModel model)
{
if (!ModelState.IsValid)
return PartialView("_Atualizar", model);
var entity = await DbContext.TipoDocumento.FirstAsync(x => x.ChaveExterna == model.Id);
entity.Nome = model.Nome;
var entry = DbContext.Entry(entity);
entry.State = EntityState.Modified;
try
{
await DbContext.SaveChangesAsync();
}
catch (DbUpdateException uex)
{
ModelState.AddModelError("", @"Houve um erro ao tentar executar a ação. Tente novamente mais tarde.");
return PartialView("_Atualizar", model);
}
catch (DbEntityValidationException ex)
{
AddErrors(ex.EntityValidationErrors);
return PartialView("_Atualizar", model);
}
catch (Exception ex)
{
ModelState.AddModelError("", @"Houve um erro ao tentar executar a ação. Tente novamente mais tarde.");
return PartialView("_Atualizar", model);
}
return Json(new { });
}
我不知道为什么会出现这种异常。我在另一个控制器中做了同样的事情,并且工作正常,但是现在,它并不想工作。
我尝试不更新该属性,但会出现相同的例外情况。
答案 0 :(得分:0)
我发现了问题。我已经使用拦截器将数据放入审核列,但我不知道为什么它在这里不起作用:
/// <summary>
/// Esse interceptor foi obtido desta url:
/// http://marisks.net/2016/02/27/entity-framework-soft-delete-and-automatic-created-modified-dates/
/// <para> Ele foi modificado para adicionar o ID do usuário e a data de Criação/Atualização/Exclusão</para>
/// Essas são as informações de auditoria que a aplicação é responsável em obter.
/// </summary>
public class EntityFrameworkAuditInterceptor : IDbCommandTreeInterceptor
{
private const string CreateUserColumnName = "UsuarioCriacaoId";
private const string CreateDateColumName = "DataCriacao";
private const string UpdateUserColumnName = "UsuarioEdicaoId";
private const string UpdateDateColumnName = "DataEdicao";
private const string DeleteUserColumnName = "UsuarioExclusaoId";
private const string DeleteDateColumnName = "DataExclusao";
private const string DeletedColumnName = "Deletado";
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace != DataSpace.SSpace)
return;
if (interceptionContext.Result is DbQueryCommandTree queryCommand)
interceptionContext.Result = HandleQueryCommand(queryCommand);
else if (interceptionContext.Result is DbInsertCommandTree insertCommand)
interceptionContext.Result = HandleInsertCommand(insertCommand);
else if (interceptionContext.OriginalResult is DbUpdateCommandTree updateCommand)
interceptionContext.Result = HandleUpdateCommand(updateCommand);
else if (interceptionContext.OriginalResult is DbDeleteCommandTree deleteCommand)
interceptionContext.Result = HandleDeleteCommand(deleteCommand);
}
private static DbCommandTree HandleInsertCommand(DbInsertCommandTree insertCommand)
{
var userId = Convert.ToInt32(((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims
.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ?? "0");
var setClauses = insertCommand.SetClauses
.Select(clause => clause.UpdateIfMatch(CreateUserColumnName, DbExpression.FromInt32(userId)))
.Select(clause => clause.UpdateIfMatch(CreateDateColumName, DbExpression.FromDateTime(DateTime.Now)))
.ToList();
return new DbInsertCommandTree(insertCommand.MetadataWorkspace, insertCommand.DataSpace,
insertCommand.Target, setClauses.AsReadOnly(), insertCommand.Returning);
}
private static DbCommandTree HandleUpdateCommand(DbUpdateCommandTree updateCommand)
{
var userId = Convert.ToInt32(((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims
.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ?? "0");
var setClauses = updateCommand
.SetClauses
.Select(clause => clause.UpdateIfMatch(UpdateUserColumnName, DbExpression.FromInt32(userId)))
.Select(clause => clause.UpdateIfMatch(UpdateDateColumnName, DbExpression.FromDateTime(DateTime.Now)))
.ToList();
return new DbUpdateCommandTree(updateCommand.MetadataWorkspace, updateCommand.DataSpace,
updateCommand.Target, updateCommand.Predicate, setClauses.AsReadOnly(), null);
}
private static DbCommandTree HandleDeleteCommand(DbDeleteCommandTree deleteCommand)
{
var setClauses = new List<DbModificationClause>();
var table = (EntityType)deleteCommand.Target.VariableType.EdmType;
if (table.Properties.All(p => p.Name != DeletedColumnName))
return deleteCommand;
var userId = Convert.ToInt32(((ClaimsIdentity)Thread.CurrentPrincipal.Identity).Claims
.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier)?.Value ?? "0");
setClauses.Add(DbExpressionBuilder.SetClause(
deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName)
.Property(DeleteUserColumnName), DbExpression.FromInt32(userId)));
setClauses.Add(DbExpressionBuilder.SetClause(
deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName)
.Property(DeleteDateColumnName), DbExpression.FromDateTime(DateTime.Now)));
setClauses.Add(DbExpressionBuilder.SetClause(
deleteCommand.Target.VariableType.Variable(deleteCommand.Target.VariableName)
.Property(DeletedColumnName), DbExpression.FromBoolean(true)));
return new DbUpdateCommandTree(deleteCommand.MetadataWorkspace, deleteCommand.DataSpace,
deleteCommand.Target, deleteCommand.Predicate, setClauses.AsReadOnly(), null);
}
private static DbCommandTree HandleQueryCommand(DbQueryCommandTree queryCommand)
{
var newQuery = queryCommand.Query.Accept(new SoftDeleteQueryVisitor());
return new DbQueryCommandTree(queryCommand.MetadataWorkspace, queryCommand.DataSpace, newQuery);
}
private class SoftDeleteQueryVisitor : DefaultExpressionVisitor
{
public override DbExpression Visit(DbScanExpression expression)
{
var table = (EntityType)expression.Target.ElementType;
if (table.Properties.All(p => p.Name != DeletedColumnName))
return base.Visit(expression);
var binding = expression.Bind();
return binding.Filter(binding.VariableType.Variable(binding.VariableName).Property(DeletedColumnName)
.NotEqual(DbExpression.FromBoolean(true)));
}
}
}
但是我在另一个系统中使用了它,并且效果很好。我将分析为什么它在这里不起作用。
编辑
我找到了原因,但是我不知道为什么会如此。我将guid(外键)更改为int(pk)来搜索该行,它的工作原理就像一个超级按钮。奇怪,我将对此进行更多分析,以了解它不适用于guid。
答案 1 :(得分:0)
问题可能是(就像在我的项目中一样)使用returning
的构造函数的DbUpdateCommandTree
参数。如果您使用null
并且在实体上计算了列(HasDatabaseGeneratedOption
),则更新将失败并抛出DbUpdateConcurrencyException
。
尝试:
return new DbUpdateCommandTree(
updateCommand.MetadataWorkspace,
updateCommand.DataSpace,
updateCommand.Target,
updateCommand.Predicate,
setClauses.AsReadOnly(),
updateCommand.Returning);