2015-08-29 3 views
1

Я работаю над приложением для отслеживания тренировок, и функциональность, которую я пытаюсь добавить, заключается в том, что пользователь может добавить тренировку с несколькими связанными данными упражнений, где каждая запись имеет связанное с ней упражнение. Каждый из них моделируется отдельный объект, т.е. тренировки, ExerciseRecord и упражнения со следующими соотношениями:Как я могу помешать Entity Framework добавлять повторяющиеся записи?

  • тренировки связан со многими ExerciseRecords.
  • ExerciseRecord относится к одному Упражнение.

Я также внедрил единицу рабочего класса и хранилища для каждого объекта (хотя я не уверен, что это необходимо - я довольно новичок в 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», таблице «dbo.Exercise», в столбце «ExerciseId». Заявление было прекращено.

Я полагаю, что моя проблема в том, что, несмотря на объекты Exercise, связанные с каждым ExerciseRecord, уже существующим в базе данных, они должны быть привязаны к контексту, чтобы Entity Framework осознавала, что они уже существуют.

Am I по правильному пути с помощью этого метода, или есть более простой способ сделать то, что я пытаюсь сделать, которая в основном сводится к:

  1. Создание объекта тренировки
  2. Для каждого Тренировка, создать х числа объектов ExerciseRecord
  3. Для каждого объекта ExerciseRecord, связать один существующий объект Упражнения
+0

Можете ли вы опубликовать свою структуру классов для упражнений, упражнений и тренировок? – drneel

+0

Узнайте, что такое ExerciseIds, прежде чем пытаться сохранить. Ошибка внешнего ключа подразумевает, что вы пытаетесь вставить ExerciseId, который существует в таблице dbo.Exercise. – Gregg

+0

Могу ли я с уверенностью предположить, что ваш 'unitOfWork' использует один экземпляр контекста для всех репозиториев? –

ответ

1

Я только что обнаружил, что этот вопрос был. В моем представлении «Создать/редактировать» есть текстовое поле с автозаполнением, которое позволяет пользователю выбирать имя упражнения. После выбора это устанавливает внешний ключ ExerciseId для ExerciseRecord.

@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 и упражнения так объект ExerciseRecord имеет ExerciseId внешний ключ, а также свойство Упражнение объекта навигации.

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 (но ни один из других полей объекта Упражнения). По-видимому, это заставило Entity Framework создавать повторяющиеся упражнения с использованием значения ExerciseName, которое было установлено, потому что все остальные поля были установлены в нулевые значения или 0.

Теперь я изменил это, изменив текстовое поле для упражнения имя в моем Create/Edit целью следующего:

<input type="text" class="autocomplete form-control" id="" [email protected]("AutoComplete") [email protected](Model.ExerciseRecords[i].Exercise == null?"":Model.ExerciseRecords[i].Exercise.ExerciseName)> 

Это все еще позволяет мне выбрать имя упражнения и установить соответствующий ExerciseId, однако не устанавливает ExerciseName свойства соответствующего объекта упражнения.

Спасибо за помощь всем.

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?

Making Do with Absent Foreign Keys

+0

Спасибо @Colin. Должен ли я также добавить код для вставки/обновления ExerciseRecords? Если я попробую код выше, он добавит объект Workout без связанных с ним ExerciseRecords. Так я понимаю, что вам нужно только установить внешние ключи, а EF должен выяснить, к какому объекту вы обращаетесь? –

Смежные вопросы