There are 3 entities - tutor, student, group, all connected with each other through many to many. I decided to do this: I declare the static variable groupForSave , and in it I put a group from the database or a blank for a new group. When adding a new student, using ajax, I add a new student to the list of groupForSave.Students , while editing, I modify the collectionForSave.Students collection object groupForSave.Students . Virtually all group collections are changed in groupForSave . At the very end, when saving changes, I call Update / Create by passing the generated groupForSave.

Problem I cannot create a new group, see the code, the line at the very beginning, the error drops even though there is not a single student in the group, the only one is a tutor, the other links are empty.

My guesses Perhaps when I take the tutor controller in the Create method from the repository, somehow its context is not destroyed. I tried to replace private dbontext = new EfDbCOntext() in all repositories with using received an error that I try to use an object from a nonexistent context in the same place when adding a group.

 //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); } 
  • There are 3 entities - tutor, student, group, all connected with each other through many to many. A somewhat dubious set of links. Able to generate contradictory relationships. Instance Instance Tutor is associated with an instance of a Student through an instance of the Group entity, but not directly connected - are these instances dependent or not? - Akina
  • @Akina Tutor has many groups and many students. A student can learn from several tutors and be in several groups. The group includes several students and may belong to several tutors at the same time. It turns out from each entity two connections (like many-to-many) with two other entities. - Nikita
  • I do not understand the subtleties of the subject area, but I feel that there is a catch somewhere. Most of all, I do not like the essence of the Group - the complete feeling that it is redundant and is in fact a pattern / rule, and not an entity. - Akina
  • @Akina ru.stackoverflow.com/questions/612586/… Please take a look, maybe you can tell something - Nikita
  • I understood what the trick was. The structure should not store the relationship between C and P, established on the basis that both these instances have the same attribute as an attribute. Only R-С ratios that are established directly, bypassing G, are stored. And the real ratio of instances of P-S (teaches or not) is calculated based on the data of the table of direct relations and the intersect of private relations according to table G. - Akina

1 answer 1

Everything turned out to be quite simple, even though I had to pretty much google and pootkladivat. Topics: garbage collector, EF virtual properties, ASP MVC 5 life cycle.

When we take an entity: var group = dbContext.Groups.First() resulting entity is bound to dbContext, i.e. he keeps track of the object he provided. There are virtual properties in entities. Virtual properties are not loaded immediately, at the time of calling group.Tutors , the context to which the entity is attached is called and the collection of the Tutor type is returned from it.

  groupForSave.Tutors = new List<Tutor>(); groupForSave.Tutors.Add(tutorRepository.GetById(tutorId)); 

So I got an item in the Tutors collection that is tied to the tutorRepository context. And therefore it is impossible to execute groupRepository.Create(groupForSave) , because the groupRepository has its own context, trying to add a tutor to it, which is tied to another context, leads to an error.

For the fix, I decided to make the Context property in the repositories:

 DbContext Context { get{return dbContext;} set{this.dbContext = value;} } 

So Before groupRepository.Create(group) I set the context for the groups from the tutor. The snag is that an instance of the controller class is created whenever you call an action method, so the context of the tutorial repository when added does not match the context of the tutorial repository that was used to initialize the group.Tutors field. The old context is referenced by the tutor, so the context was not deleted by the garbage collector, although the controller instance was destroyed.

The solution was to declare a static contextForSave variable, which is initiated by the desired context in the Create method and subsequently used whenever working with groupRepository. Also this context is used for the same reasons for studentRepository.

In short, it looks like this:

 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); //... } 
  • You have upgraded your repositories. Just accept the context in the repository designer as a parameter. - Pavel Mayorov
  • @PavelMayorov Added a constructor with a parameter. I have not greatly complicated my repositories, because they are inherited from the abstract repository, it took 4 lines for the property and 3 for the second constructor :) - Nikita