如何防止实体框架添加重复条目
本文关键字:添加 何防止 实体 框架 | 更新日期: 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关联的练习对象,但它们需要附加到上下文,以便实体框架意识到它们已经存在。
我的方法是正确的吗?还是有更简单的方法来完成我想做的事情,基本上可以归结为:
- 创建锻炼对象
- 对于每个Workout,创建x个数的ExerciseRecord对象
- 对于每个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();
引用:
为什么实体框架重新插入现有对象到我的数据库?
使用不存在的外键