我有我无法解决的问题。这是场景。我有两个模型,第一个是问题,第二个是答案。此外,我有DAL、BusinessEntities(视图模型)和BusinessServices类库,具有UnitOfWork和GenericRepository模式。当我在一个模型工作时编辑(更新)它时,问题与以这种方式编辑操作有关,但当我尝试更新具有3个答案作为列表属性的问题模型时,我得到了这个异常
//异常开始附加“测验”类型的实体。DAL.答案”失败,因为另一个相同类型的实体已经具有相同的主键值。如果图形中的任何实体具有冲突的键值,则使用“附加”方法或将实体的状态设置为“未更改”或“修改”时可能会发生这种情况。这可能是因为某些实体是新的并且尚未收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“修改”。//异常结束
GenericRepository类中发生异常。
public class GenericRepository<TEntity> where TEntity : class
{
internal QuizContext Context;
internal DbSet<TEntity> DbSet;
/// <summary>
/// Public Constructor,initializes privately declared local variables.
/// </summary>
/// <param name="context"></param>
public GenericRepository(QuizContext context)
{
this.Context = context;
this.DbSet = context.Set<TEntity>();
}
public virtual void Update(TEntity entityToUpdate)
{
DbSet.Attach(entityToUpdate); //exception is invoked here
Context.Entry(entityToUpdate).State = EntityState.Modified;
}
}
我检查了所有数据是否从视图发布到操作,并且确实如此。
这是从编辑操作调用的方法
public class QuestionServices : IQuestionServices
{
private UnitOfWork _unitOfWork;
public void UpdateQuestion(QuestionEntity questionEntity)
{
var questionDb = new Question
{
Id = questionEntity.Id,
Name = questionEntity.Name
};
var answersDb = _unitOfWork.AnswerRepository.GetMany(a =>
a.QuestionId == questionDb.Id).ToList();
foreach (var a in questionEntity.Answers)
{
answersDb.Add(new Answer()
{
Id = a.Id,
Name = a.Name,
IsTrue = a.IsTrue,
QuestionId = a.QuestionId,
Question = questionDb
});
}
unitOfWork.QuestionRepository.Update(questionDb);
foreach (var answer in answersDb)
{
_unitOfWork.AnswerRepository.Update(answer);
}
_unitOfWork.Save();
}
编辑视图
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>QuestionEntity</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
</div>
</div>
@for (int i=0; i<Model.Answers.Count; i++)
{
@Html.EditorFor(m=>m.Answers[i])
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
answerEntity的编辑器模板
<div class="form-horizontal"/>
@Html.HiddenFor(model=>model.Id)
@Html.HiddenFor(model=>model.QuestionId)
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.IsTrue, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
<div class="checkbox">
@Html.EditorFor(model => model.IsTrue)
@Html.ValidationMessageFor(model => model.IsTrue, "", new { @class = "text-danger" })
</div>
</div>
</div>
最后是动作方法
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Name,Answers")]
QuestionEntity questionEntity)
{
if (ModelState.IsValid)
{
//db.Entry(questionEntity).State = EntityState.Modified;
//db.SaveChanges();
_questionServices.UpdateQuestion(questionEntity);
return RedirectToAction("Index");
}
return View(questionEntity);
}
我相信这个错误是弹出的,因为我必须告诉ef我想更新答案,但ef认为它们是新的,应该创建它们而不是更新女巫会导致相同的主键错误,或者,我在内存中有更多的答案副本,这会混淆ef。
感谢任何帮助。
编辑https://github.com/StefanIvovic/Quiz看看
这根本不是故意冒犯,因为老实说,这可能不是你的错。外面有这么多坏消息。尽管如此,这段代码绝对是悲剧。你有三个主要问题。
>
public class QuestionViewModel
{
public string Name { get; set; }
public List<Answer> Answers { get; set; }
}
请注意:1)不包括Id。您不应允许发布id。id应该来自URI,因为这就是它成为“通用资源标识符”的原因。2) 同样,这里也应该使用视图模型,因此您的属性应该最像是列表
永远,永远,永远,永远不要将帖子创建的任何内容直接保存到数据库中。这实际上可以通过使用视图模型来解决,因为您必须保存与发布内容不同的内容。然而,您应该始终将post数据映射到要保存的实体上。这在进行编辑时更为重要,因为您应该始终从数据库中提取新实体,修改该实例,然后保存该实例。这样,即使不使用视图模型,也不需要绑定,因为用户实际上无法获取他们发布的任何内容以保存到数据库,除非您明确允许,将发布的属性映射到要保存的实体属性。
最后,对于像Entity Framework这样的ORM,存储库/工作单元模式是完全多余的。EF实际上已经实现了这些模式:DbContext
是您的工作单元,每个DbSet
都是一个存储库。在此基础上添加一个层只会混淆正在发生的事情,从而使您的应用程序更难维护。我听到了一百万个借口,为什么人们认为您仍然应该实现这些模式,但它们要么被误导了(“没有它就不能测试!”错了。EF是100%可测试的。)或者通过不同的模式更好地解决(例如命令查询责任隔离(CQRS)或服务层模式)。
错误消息:附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 问题:我如何以类似于下面代码中AttachActivity方法所示的方式附加实体? 我必须假设上面错误消息的“另一个实体”部分指的是内存中存在但超出范围的对象(?)。我之所以注意到这一点,是因为我尝试附加的实体类型的DBSet的本地属性返回零。 我有理由相信实体不存在于上下文中,因为我遍历代码并在创建上下文时观察上下文。实
让我快速描述一下我的问题。 我有5个客户的5个数据库,每个数据库都有一个名为SubnetSettings的表。 我已经创建了一个下拉列表来选择一个客户,并将显示属于所选客户的SubnetSSet表,并允许我创建、编辑和删除。 我可以毫无问题地创建、删除,但当我想编辑数据时,它会带来错误: /运输管理系统应用程序中的服务器错误。 附加“CFS”类型的实体。领域实体。SubnetSettings“失败
错误消息: 正在附加“FaridCRMData”类型的实体。模型。“Customer”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的 我的代码: FactorService. BaseService.cs:
错误: 其他信息:附加“实体”类型的实体失败。客户”,因为另一个相同类型的实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则使用“附加”方法或将实体的状态设置为“未更改”或“修改”时可能会发生这种情况。这可能是因为某些实体是新的并且没有收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体来跟踪图,然后根据需要将非新实体的状态设置为“未更改”或“修改”。 我的代码: 我是这
我的WebApi应用程序有一个小问题。当我将新实体添加到我的数据库时,会出现这个问题。 实体: 要在其中向数据库中添加新赌注实体的代码: 在此位置,将我的实体添加到DB中(DB:我的SQL) 调用save方法时,代码会引发以下异常: 附加“WebCar. Domain. Entities. Lot”类型的实体失败,因为另一个相同类型的实体已经具有相同的主键值。如果图形中的任何实体具有冲突的键值,则
我正在使用automapper重构一些代码,请参见下面注释掉的旧代码。 编辑:请注意,属性对象是从基础设置的,因此建议的重复问题不适用。 当状态设置为modified时,我得到以下异常:; 附加“M.Survey”类型的实体。ServiceLayer。模型属性“”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“U