当前位置: 首页 > 知识库问答 >
问题:

附加类型为“测验”的实体。DAL。答案”失败,因为另一个相同类型的实体已经具有相同的主键值

胥英奕
2023-03-14

我有我无法解决的问题。这是场景。我有两个模型,第一个是问题,第二个是答案。此外,我有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看看

共有1个答案

红甫
2023-03-14

这根本不是故意冒犯,因为老实说,这可能不是你的错。外面有这么多坏消息。尽管如此,这段代码绝对是悲剧。你有三个主要问题。

>

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