如何防止实体框架添加重复条目

本文关键字:添加 何防止 实体 框架 | 更新日期: 2023-09-27 18:10:19

我正在开发一个跟踪锻炼的应用程序,我试图添加的功能是,用户可以添加一个有几个相关锻炼记录的锻炼,其中每个记录都有一个相关的锻炼。它们中的每一个都由一个单独的实体建模,即Workout、ExerciseRecord和Exercise,它们具有以下关系:

  • 与许多锻练记录相关。
  • ExerciseRecord与一个练习相关。

我还为每个实体实现了一个工作类和存储库单元(尽管我不确定这是否必要-我是ASP的新手)。净MVC)。

添加锻炼的代码如下所示:

[HttpPost]
public ActionResult Edit(WorkoutViewModel viewModel)        
{        
    var exerciseRecordList = viewModel.ExerciseRecords;
    foreach (ExerciseRecord r in exerciseRecordList)
    {
        var e = exerciseList.Exercises
            .Where(ex => ex.ExerciseId == r.ExerciseId)
            .FirstOrDefault();
        r.Exercise = e;
        if (viewModel.WorkoutId != 0)
        {
            r.WorkoutId = viewModel.WorkoutId;
        }
    };
    Workout workout;
    if (viewModel.WorkoutId == 0)
    {
        workout = new Workout
        {
            WorkoutId = viewModel.WorkoutId,
            WorkoutDate = viewModel.WorkoutDate,
            Duration = viewModel.Duration,
            Exercises = exerciseRecordList
        };
    }
    else
    {
        workout = unitOfWork.WorkoutRepository.GetByID(viewModel.WorkoutId);
        workout.WorkoutDate = viewModel.WorkoutDate;
        workout.Duration = viewModel.Duration;
        workout.Exercises = exerciseRecordList;
    }
    if (ModelState.IsValid)
    {
        if (workout.WorkoutId == 0)
        {
            unitOfWork.WorkoutRepository.Insert(workout);
            unitOfWork.Save();
        }
        else
        {
            unitOfWork.WorkoutRepository.Update(workout);
            unitOfWork.Save();
        }
        TempData["message"] = "Workout has been saved";
        return RedirectToAction("List");
    }
    else
    {
        // When there has been a problem with the workout data values
        TempData["message"] = "Unable to save workout";
        return View(viewModel);
    }
}

这段代码在创建新的锻炼时工作,但是它似乎向数据库添加了重复的锻炼。我也不能使用它来编辑现有的锻炼,因为我收到以下错误:

INSERT语句与外键约束"FK_dbo.ExerciseRecords_dbo.Exercises_ExerciseId"冲突。冲突发生在数据库"WorkoutTracker"表"dbo "中。"Exercise",列"ExerciseId"。语句已终止

我认为我的问题是,尽管与数据库中已经存在的每个ExerciseRecord关联的练习对象,但它们需要附加到上下文,以便实体框架意识到它们已经存在。

我的方法是正确的吗?还是有更简单的方法来完成我想做的事情,基本上可以归结为:

  1. 创建锻炼对象
  2. 对于每个Workout,创建x个数的ExerciseRecord对象
  3. 对于每个ExerciseRecord对象,关联一个现有的Exercise对象

如何防止实体框架添加重复条目

我刚刚发现了问题所在。我的Create/Edit视图有一个自动完成的文本框,允许用户选择练习名称。在选择之后,它将为ExerciseRecord设置ExerciseId外键。

@Html.TextBoxFor(m => m.ExerciseRecords[i].Exercise.ExerciseName, new { @class = "autocomplete form-control", id = "", name = "ExerciseName_" + i, data_url = @Url.Action("AutoComplete") })
@Html.HiddenFor(m => m.ExerciseRecords[i].ExerciseId, new { id = "" + i, name = "ExerciseId_" + i, @class = "hidden-id" })

在ExerciseRecord和Exercises之间有一个多对一的关系,所以一个ExerciseRecord对象有一个ExerciseId外键和一个Exercise对象导航属性。

public class ExerciseRecord
{
    public int ExerciseRecordId { get; set; }
    public int Reps { get; set; }
    public int Sets { get; set; }
    public decimal Weight { get; set; } 
    public virtual Exercise Exercise { get; set; }
    public int ExerciseId { get; set; }
    public virtual Workout Workout { get; set; }  
    public int WorkoutId { get; set; }
}

发生的事情是,在提交表单时,设置了ExerciseId外键和关联的Exercise。ExerciseName(但没有其他的Exercise对象字段)。这似乎导致实体框架然后使用设置的练习名创建重复练习,因为所有其他字段都设置为空值或0。

我现在修改了这一点,在我的创建/编辑视图中将练习名称的文本框更改为以下内容:

<input type="text" class="autocomplete form-control" id="" data-url=@Url.Action("AutoComplete") value=@(Model.ExerciseRecords[i].Exercise == null?"":Model.ExerciseRecords[i].Exercise.ExerciseName)>

这仍然允许我选择一个运动名称并设置相应的ExerciseName,但是不设置相关运动对象的ExerciseName属性。

谢谢大家的帮助。

试着按照你想要做的事情的顺序来做:

   //Find (or Create) a `Workout` entity
   if (viewModel.WorkoutId == 0)
   {
      workout = new Workout();
      unitOfWork.WorkoutRepository.Insert(workout);
      unitOfWork.SaveChanges();   //You need to save here to ensure the ID is set
   }
   else
   {
      workout = unitOfWork.WorkoutRepository.GetByID(viewModel.WorkoutId);
   }
   workout.WorkoutDate = viewModel.WorkoutDate;
   workout.Duration = viewModel.Duration;
   //You appear to already have a set of ExerciseRecords already 
   //linked to an Exercise, so now link them to the workout
   foreach (ExerciseRecord r in viewModel.ExerciseRecords)
   {
      //I can't work out why you are fetching Exercises by the
      //ExerciseRecord's ExerciseId then using that to set the Exercise
      //property. But I think that is why you are getting duplicates,
      //so I have removed that fetch. The foreign key is sufficient and it 
      //is already set
      r.WorkOutID = workout.ID; 
   };
    unitOfWork.SaveChanges();

引用:

为什么实体框架重新插入现有对象到我的数据库?

使用不存在的外键