2

I have an entity which holds a list of entities (same as root entity) to represent a Folder structure:

public class SopFolder
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime? LastUpdated { get; set; }
    public int Status { get; set; }
    public virtual ICollection<SopField> SopFields { get; set; }
    public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
    public virtual ICollection<SopBlock> Blocks { get; set; }
    public virtual ICollection<SopReview> Reviews { get; set; }
}

This entity is stored in my DB using Code-First Approach which is working fine. I then print the entity to a KendoUI Treeview, let the user modify it and on "save" post it back to the Server to an Action as IEnumerable<TreeViewItemModel> items.

I then look for the ROOT entity with all it's children (there is only one root) and convert it back into an SopFolder object.

To get the full object updated in the database I do the following:

 List<SopFolder> sopfolderlist = ConvertTree(items.First());

        SopFolder sopfolder = sopfolderlist[0];

        if (ModelState.IsValid)
        {
            SopFolder startFolder = new SopFolder { Id = sopfolder.Id };

            //db.SopFolders.Attach(startFolder);
           // db.SopFolders.Attach(sopfolder);

            startFolder.Name = sopfolder.Name;
            startFolder.LastUpdated = sopfolder.LastUpdated;
            startFolder.SopFields = sopfolder.SopFields;
            startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;
            startFolder.Status = sopfolder.Status;

            db.Entry(startFolder).State = EntityState.Modified;

            db.SaveChanges();
            return Content("true");
        }

However this is not working. The model is not updated at all. If I shift the "entityState.Modified" before the modifications, it just creates a complete fresh duplicate of my data in the database (modified of course).

Is my approach correct or do I have to go a different path? What am I missing here? I guess there is another "hidden" id which lets the EF map the entities to the db entries but I am not sure about this. Thanks for help!

UPDATE: Instead of creatinga new instance of SopFolder I also tried db.SopFolders.Find(sopfolder.Id) and this works for entries with no children. If I have entities with children, it creates a duplicate.

Regards, Marcus

marcus.braun
  • 52
  • 11
  • I can not understant this aproach. But I use this such: var startFolder = entityRepository.Find(sopfolder.Id); then startfolder.Name = sopfolder.Name ... you must appeal same entity – Elvin Mammadov Jul 29 '13 at 10:37
  • I tried `SopFolder startFolder = db.SopFolders.Find(sopfolder.Id)` but this gave me a complete duplicate of the structure instead of just editing the existing one. – marcus.braun Jul 29 '13 at 10:49
  • I think it is immposible, @user2014432. Because this means that your data already been updated. – Elvin Mammadov Jul 29 '13 at 10:51
  • Check my update above. It works with find for single entries but not for Folders with children. I will therefore Change my Approach and iterate through each entity and modify + save it to the database separately. Maybe that works. – marcus.braun Jul 29 '13 at 10:58

3 Answers3

1

This is typical Disconnected Graph scenario. Please see this question for possible solutions: Disconnected Behavior of Entity Framework when Updating Object Graph

You have already figure out the first solution - that is: update entities separately. Actually, what you should do is to fetch the original data from database and then do comparison of what have changed. There are some generic ways of doing that, some of them are described in "Programming EF DbContext" book by J.Lerman, which I strongly recommend to you before doing more coding using EF.

P.S. IMHO this is the worse downside of EF.

Community
  • 1
  • 1
Mariusz.W
  • 1,337
  • 12
  • 19
0

I recommend you to create your entity model with ParentId, not children object list. When you need treeview model collect it with recursive function from database.

public class SopFolder
{
    public int Id { get; set; }
    public string Name { get; set; }
    public DateTime? LastUpdated { get; set; }
    public int Status { get; set; }
    public virtual ICollection<SopField> SopFields { get; set; }

    //public virtual ICollection<SopFolder> SopFolderChildrens { get; set; }
    public int? ParentFolderId { get; set; }

    public virtual ICollection<SopBlock> Blocks { get; set; }
    public virtual ICollection<SopReview> Reviews { get; set; }
}

When you create children folders, select it's parent, so collect your data. In childrens case try this :

List<SopFolder> sopfolderlist = ConvertTree(items.First());

        SopFolder sopfolder = sopfolderlist[0];

        if (ModelState.IsValid)
        {
            SopFolder startFolder = new SopFolder { Id = sopfolder.Id };

            //db.SopFolders.Attach(startFolder);
           // db.SopFolders.Attach(sopfolder);

            startFolder.Name = sopfolder.Name;
            startFolder.LastUpdated = sopfolder.LastUpdated;
            startFolder.SopFields = sopfolder.SopFields;
            startFolder.SopFolderChildrens = sopfolder.SopFolderChildrens;

            foreach (var child in sopfolder.SopFolderChildrens)
            {
               db.SopFolders.CurrentValues.SetValues(child);
               db.SaveChanges();
            }

            startFolder.Status = sopfolder.Status;
            db.Entry(startFolder).State = EntityState.Modified;
            db.SaveChanges();
            return Content("true");
        }
Jeyhun Rahimov
  • 3,711
  • 6
  • 44
  • 88
  • Not sure if I am adressing the right Thing but in my code-first approach, the IEnumerable SopFolderChildren List automatically creates a parent ID called SopFolder_Id (with the parent Folder id) in the database. Do you mean this? – marcus.braun Jul 29 '13 at 11:13
  • Why would you recommend this Approach instead of using the child collection? What about exposing the parent SopFolderID within the object, wouldn't that be a proper way as well? Then I have the child collection and the parent id. I could not figure out how, though. – marcus.braun Jul 29 '13 at 15:17
  • Because in your case, you also should edit your child elements. Check edited answer. – Jeyhun Rahimov Jul 29 '13 at 15:37
  • And what happens when some of SopFolderChildren are removed? – Mariusz.W Jul 30 '13 at 09:23
0

Replace SopFolder startFolder = new SopFolder { Id = sopfolder.Id }; with

 SopFolder startFolder = db.SopFolders.FirstOrDefault(s=>s.Id.Equals(sopfolder.Id));

 // then validate if startFolder != null
Kosala W
  • 2,073
  • 1
  • 14
  • 20