我正在开发用于跟踪锻炼的应用程序以及我尝试添加的功能,以便用户可以添加具有多个相关锻炼记录的锻炼,其中每个记录都有相关的锻炼。这些中的每一个都由一个单独的实体建模,即Workout,ExerciseRecord和Exercise,具有以下关系:
我还为每个实体实现了一个工作类和存储库单元(虽然我不确定这是否必要 - 我对ASP.Net 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语句与FOREIGN KEY约束冲突" FK_dbo.ExerciseRecords_dbo.Exercises_ExerciseId"。冲突发生在数据库" WorkoutTracker",table" dbo.Exercise",column' ExerciseId'中。 该声明已被终止。
我认为我的问题是尽管与数据库中已经存在的每个ExerciseRecord相关联的Exercise对象,但是它们需要附加到上下文,以便实体框架知道它们已经存在。
我是否采用这种方法走上了正确的轨道,或者是否有更容易的方法来实现我尝试做的事情,这基本上归结为:
答案 0 :(得分:1)
我刚刚发现了问题所在。我的“创建/编辑”视图有一个带有自动完成功能的文本框,允许用户选择练习名称。选择后,然后为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" })
ExerciseRecords和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对象字段)。这似乎导致实体框架使用设置的ExerciseName创建重复练习,因为所有其他字段都设置为空值或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)>
这仍然允许我选择一个练习名称并设置相应的ExerciseId,但是不设置相关练习对象的ExerciseName属性。
感谢大家的帮助。
答案 1 :(得分:0)
尝试按照您尝试做的顺序执行此操作基本归结为:
//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();
参考文献:
Why does Entity Framework Reinsert Existing Objects into My Database?