Есть 3 сущности - репетитор, студент, группа, все связаны друг с другом через многие ко многим. Решил сделать так: объявляю статическую переменную groupForSave
, и в неё ложу группу из БД либо болванку для новой группы. При добавлении нового студента, с помощью ajax добавляю нового студента в список groupForSave.Students
, при редактировании соответственно изменяю объект коллекции groupForSave.Students
. Фактически все коллекции группы изменяются в groupForSave
. В самом конце, при сохранении изменений, вызываю Update/Create передав сформированный groupForSave.
Проблема Не могу создать новую группу, см. код, строчка в самом начале, ошибка падает даже при том, что в группе нету ни одного студента, единственное указан репетитор, остальные связи пустые.
Мои догадки Возможно, когда я беру репетитора контроллер в методе Create
из репозитория, каким-то образом его контекст не уничтожается. Пробовал заменить во всех репозиториях private dbontext = new EfDbCOntext()
на using
получил ошибку, что пробую использовать объект из несуществующего контекста, в том же месте при добавлении группы.
//entity framework часть
class GroupRepository {
dbContext = new EfDbContext(); //Ошика как-то связана с контекстами
//упрощено, ошибка при добавлении новой записи
public Group Create(Group obj) {
dbContext.Groups.Add(obj); //Здесь падает ошибка
}
}
/*
... Классы-репозитории с аналогичным методом Create для остальных сущностей(entity)
*/
class Group {
public virtual List<Tutor> Tutors;
public virtual List<Student> Students;
}
class Student {
public virtual List<Group> Groups;
public virtual List<Tutor> Tutors;
}
class Tutor {
public virtual List<Group> Groups;
public virtual List<Student> Students;
}
//Контроллер
GroupController() {
GroupRepository groupRepository = new GroupRepository();
TutorRepository tutorRepository = new TutorRepository();
StudentRepository studentRepository = new StudentRepository();
}
[HttpPost]
public JsonResult AddStudent(Student student)
{
//because id is 0 we should define if we need to create new student or it aleady exists in database
var stud = studentRepository.GetByMail(student.Email);
if(stud != null)
{
student = stud;
}else
{
student.ID = idCounter++;
}
groupForSave.Students.Add(student);
return Json(student);
}
public ActionResult Create(int tutorId)
{
EditGroupViewModel model = new EditGroupViewModel();
groupForSave = new Group();
groupForSave.Journals = new List<Journal>();
groupForSave.Students = new List<Student>();
groupForSave.Tutors = new List<Tutor>();
groupForSave.Tutors.Add(tutorRepository.GetById(tutorId));
model.name = "Новая группа " + groupRepository.GetTutorGroups(tutorId).Count();
model.tutorId = tutorId;
redirectAfterCreate = Request.UrlReferrer.ToString();
return View("Edit", model);
}
public JsonResult Save(Group group)
{
//return Json(group, JsonRequestBehavior.AllowGet);
groupForSave.Name = group.Name;
groupRepository.CreateWithContext(groupForSave, tutorRepository.Context);
return Json(new { redirectUrl = redirectAfterCreate }, JsonRequestBehavior.AllowGet);
}
Всё оказалось довольно просто, хоть и пришлось изрядно погуглить и поотлаживать. Темы сборщик мусора, виртуальные свойства EF, жизненный цикл контроллера ASP MVC 5.
Когда мы берём сущность: var group = dbContext.Groups.First()
полученная сущность привязана к dbContext, т.е. он отслеживает объект который предоставил. В сущностях есть виртуальные свойства. Виртуальные свойства сразу не прогружены, в момент вызова group.Tutors
происходит обращение к контексту, к которому привязана сущность и из него возвращается коллекция типа Tutor
.
groupForSave.Tutors = new List<Tutor>();
groupForSave.Tutors.Add(tutorRepository.GetById(tutorId));
таким образом я получил в коллекции Tutors элемент, который завязан на контекст tutorRepository. И поэтому нельзя выполнить groupRepository.Create(groupForSave)
, ведь в groupRepository есть свой контекст, попытка добавить в него tutor, который завязан на другой контекст приводит к ошибке.
Для исправления я решил, сделать свойство Context в репозиториях:
DbContext Context {
get{return dbContext;}
set{this.dbContext = value;}
}
Т.о. перед groupRepository.Create(group)
я устанавливаю группам контекст из репетитора. Загвоздка в том, что экземпляр класса контроллера создаётся всякий раз при вызове метода действия, поэтому контекст репетиторской репозитории при добавлении не соответствует контексту репетиторского репозитория, который был использован для начальной инициализации поля group.Tutors. На старый контекст ссылается репетитор,поэтому контекст не был удалён сборщиком мусора, хотя экземпляр контроллера был уничтожен.
Решением стало объявление статической переменной contextForSave, которая инициилизируется нужным контекстом в методе Create и в последствии используется всякий раз при работе с groupRepository. Также этот контекст по тем же причинам используется для studentRepository
кратко это выглядит так:
public class GroupController : Controller
{
private GroupRepository groupRepository = new GroupRepository();
private TutorRepository tutorRepository = new TutorRepository();
private StudentRepository studentRepository = new StudentRepository();
public static Group groupForSave;
private static EFDbTutorProject contextForSave;
public ActionResult Edit(int groupId, int tutorId)
{
contextForSave = tutorRepository.Context;
groupRepository.Context = contextForSave; //as all classes have different context, we need one to detect modified students
//...
}
public ActionResult Create(int groupId, int tutorId)
{
contextForSave = tutorRepository.Context;
groupRepository.Context = contextForSave; //as all classes have different context, we need one to detect modified students
//...
}
public ActionResult AddStudent(Student student)
{
//because id is 0 we should define if we need to create new student or it aleady exists in database
studentRepository.Context = contextForSave;
var stud = studentRepository.GetByMail(student.Email);
//...
}
public JsonResult Save(Group group)
{
//return Json(group, JsonRequestBehavior.AllowGet);
groupForSave.Name = group.Name;
groupRepository.CreateWithContext(groupForSave, contextForSave);
//...
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Добрый день! Мне нужно вывести в DataGridView колонки с именами субъектов (класс Subject) из коллекции subjects, но при этом присвоить колонке в поле Tag идентификатор...
Как лучше всего организовать страничное представление данных в гриде? Примерно 10 000 строк
Исключение типа "SystemInvalidOperationException" возникло в EntityFramework
У меня появилась проблема мне нужно отобразить изображение с камеры используя api lifecam в качестве примера я выбрал этот код