title: How do I gracefully reattach an Entity Framework 5 POCO structure and save it?
body: <p>I am making an MVC4 web application using Entity Framework 5 (Database-first with generated POCOs) for data access.</p>
  
  <p>In the app, the user goes through several screens, creating or editing a document (called a 'case study'). When they arrive at the final screen, their document exists as a CaseStudy POCO in memory, and everything is great until it is time to save this structure to the database.</p>
  
  <p>To store the document, I have defined several database tables, which in turn map to EF POCOs used by the business layer, which is then consumed by the MVC controllers. As such, short-lived DbContexts are used to retrieve POCOs and store them in session between requests.</p>
  
  <p>As a result, the save screen must save the contents of this POCO that has navigational properties to existing table data (Category, Layout, and Sections tables), and also added or updated data (CaseStudySections and the CaseStudy itself). So all of the POCOs are either new, or the context used to retrieve them has long been disposed. In other words, they are all 'detached'.</p>
  
  <p>What is unusual about this post is that I already have a working solution in hand. The problem is that it is bulky, brittle, and inelegant. I am posting the code below. Note the iteration through sub-collections, the explicit adds and attaches, having to get an entry object and mark individual properties as modified just so they will be updated, and the awful song and dance at the end to get the AdditionalMaterials collection synced up. If this is what is required to deal with detached POCOs in EF5 I will be disappointed.</p>
  
  <p>Am I missing something here? Is this consistent with best practices? Is there a more graceful and/or concise way to attach a structure of POCOs and insert/update?</p>
  
  <p>The code to save a case study:</p>
  
  <pre><code>public void SaveCaseStudy(CaseStudy caseStudy)
  {
      foreach (var s in caseStudy.CaseStudySections)
      {
          this.Entities.Sections.Attach(s.Section);
  
          if (s.CreatedByRefId == default(Guid))
          {
              s.CreatedByRefId = this.UserRefId;
              s.CreatedTime = DateTime.Now;
              this.Entities.CaseStudySections.Add(s);
          }
          else
          {
              this.Entities.CaseStudySections.Attach(s);
              var entry = this.Entities.Entry(s);
              entry.Property(e =&gt; e.TextData).IsModified = true;
              entry.Property(e =&gt; e.BinaryData).IsModified = true;
          }
  
          s.LastModifiedByRefId = this.UserRefId;
          s.LastModifiedTime = DateTime.Now;
      }
  
      foreach (var m in caseStudy.AdditionalMaterials)
      {
          if (m.CreatedByRefId == default(Guid))
          {
              m.CreatedByRefId = this.UserRefId;
              m.CreatedTime = DateTime.Now;
              this.Entities.AdditionalMaterials.Add(m);
          }
          else
          {
              this.Entities.AdditionalMaterials.Attach(m);
          }
  
          m.LastModifiedByRefId = this.UserRefId;
          m.LastModifiedByTime = DateTime.Now;
      }
  
      this.Entities.Layouts.Attach(caseStudy.Layout);
      this.Entities.Categories.Attach(caseStudy.Category);
  
      if (caseStudy.CreatedByRefId != default(Guid))
      {
          this.Entities.CaseStudies.Attach(caseStudy);
          var entry = this.Entities.Entry(caseStudy);
          entry.Property(e =&gt; e.CaseStudyName).IsModified = true;
          entry.Property(e =&gt; e.CaseStudyTitle).IsModified = true;
      }
      else
      {
          this.Entities.CaseStudies.Add(caseStudy);
          caseStudy.CreatedByRefId = this.UserRefId;
          caseStudy.CreatedTime = DateTime.Now;
      }
  
      caseStudy.LastModifiedByRefId = this.UserRefId;
      caseStudy.LastModifiedTime = DateTime.Now;
  
      if (caseStudy.CaseStudyStatus != (int)CaseStudyStatus.Personalized)
      {
          caseStudy.CaseStudyStatus = (int)CaseStudyStatus.PendingApproval;
      }
  
      caseStudy.ApprovedByRefId = null;
      caseStudy.ApprovedTime = null;
      this.Entities.SaveChanges();
  
      var existingAdditionalMaterialRefIds = caseStudy.AdditionalMaterials
          .Select(m =&gt; m.AdditionalMaterialRefId)
          .ToArray();
  
      var additionalMaterialsToRemove = this.Entities.AdditionalMaterials
          .Where(m =&gt; 
              m.CaseStudyRefId == caseStudy.CaseStudyRefId &amp;&amp; 
              !existingAdditionalMaterialRefIds.Contains(m.AdditionalMaterialRefId))
          .ToArray();
  
      foreach (var additionalMaterialToRemove in additionalMaterialsToRemove)
      {
          this.Entities.AdditionalMaterials.Remove(additionalMaterialToRemove);
      }
  
      this.Entities.SaveChanges();
  }
  </code></pre>