title: Map generic child to generic parent from stored procedure results
body: <p>I use a similar approach to the one described in <a href="http://www.khalidabuhakmeh.com/entity-framework-6-multiple-result-sets-with-stored-procedures" rel="nofollow noreferrer">this</a> post to map multiple stored procedure result sets to a parent and child entity using Entity Framework. However, every implementation of this approach that I've seen assigns the results of subsequent result sets (after the first) to properties of the parent entity directly; I'd like to accomplish this in a generic fashion.</p>
  
  <p>So far I've been able to build a wrapper that successfully maps results in a one-to-many relationship. I don't need to care about enforcing the foreign key relationship in this case because the stored procedure handles that, but when selecting many parent entities, I need to somehow enforce the relationship.</p>
  
  <p>How can I leverage the foreign key relationship in Entity Framework (using reflection, presumably) to enforce the mapping?</p>
  
  <p>Here's what I have:</p>
  
  <p>Entities</p>
  
  <pre><code>class FooEntity
  {
       [Key]
       int Id { get; set;}
  
       ICollection&lt;BarEntity&gt; Bars { get; set; }
  }
  
  class BarEntity
  {
       [Key]
       int Id { get; set;}
  
       int FooId { get; set; }
  
       [ForeignKey("FooId")]
       virtual FooEntity Foo { get; set; }
  }
  </code></pre>
  
  <p>Generic stored procedure translator:</p>
  
  <pre><code>public class StoredProcedureTranslator&lt;TDbContext&gt; : IDisposable where TDbContext : DbContext, new()
  {
      private DbCommand command;
  
      public StoredProcedureTranslator()
      {
          this.Context = new TDbContext();
      }
  
      public DbContext Context { get; set; }
  
      public DbDataReader Reader { get; set; }
  
      public StoredProcedureResult&lt;T, TDbContext&gt; Translate&lt;T&gt;(string procedureName, SqlParameter[] parameters) where T : class
      {
          this.command = this.Context.Database.Connection.CreateCommand();
          this.command.CommandText = procedureName;
          if (parameters != null)
          {
               this.command.Parameters.AddRange(parameters);
          }
  
          this.command.CommandType = CommandType.StoredProcedure;
  
          this.Context.Database.Connection.Open();
  
          this.Reader = this.command.ExecuteReader();
  
          ObjectResult&lt;T&gt; results = ((IObjectContextAdapter)this.Context).ObjectContext.Translate&lt;T&gt;(this.Reader);
          return new StoredProcedureResult&lt;T, TDbContext&gt;(results.ToList(), this);
      }
  
      public void Dispose()
      {
          this.Context.Dispose();
      }
  }
  </code></pre>
  
  <p>Generic stored procedure result:</p>
  
  <pre><code>public class StoredProcedureResult&lt;T, TDbContext&gt; : IEnumerable&lt;T&gt;
      where T : class where TDbContext : DbContext, new()
  {
      private readonly IEnumerable&lt;T&gt; results;
  
      private readonly StoredProcedureTranslator&lt;TDbContext&gt; translator;
  
      public StoredProcedureResult(IEnumerable&lt;T&gt; results, StoredProcedureTranslator&lt;TDbContext&gt; translator)
      {
          this.results = results;
          this.translator = translator;
      }
  
      public StoredProcedureResult&lt;T, TDbContext&gt; Include&lt;TChild&gt;(Expression&lt;Func&lt;T, IEnumerable&lt;TChild&gt;&gt;&gt; member) where TChild : class
      {
          T result = this.results.FirstOrDefault();
  
          if (result == null)
          {
              return null;
          }
  
          if (!this.translator.Reader.NextResult())
          {
              return new StoredProcedureResult&lt;T, TDbContext&gt;(new List&lt;T&gt; { result }, this.translator);
          }
  
          // TODO: do some stuff with the fks
          ObjectResult&lt;TChild&gt; childResults = ((IObjectContextAdapter)this.translator.Context).ObjectContext.Translate&lt;TChild&gt;(this.translator.Reader);
          var prop = member.Body as MemberExpression;
          if (prop == null)
          {
              throw new Exception("blah blah");
          }
  
          var property = prop.Member as PropertyInfo;
          if (property == null)
          {
              throw new Exception("blah blah");
          }
  
          property.SetValue(result, childResults.ToList(), null);
  
  
          return new StoredProcedureResult&lt;T, TDbContext&gt;(new List&lt;T&gt; { result }, this.translator);
      }
  
      #region IEnumerable Impl
  
      public IEnumerator GetEnumerator()
      {
          return this.results.GetEnumerator();
      }
  
      IEnumerator&lt;T&gt; IEnumerable&lt;T&gt;.GetEnumerator()
      {
          return (IEnumerator&lt;T&gt;)this.GetEnumerator();
      }
  
      #endregion
  }
  </code></pre>
  
  <p>Usage</p>
  
  <pre><code>using (var spt = new StoredProcedureTranslator&lt;DbContext&gt;())
  {
       FooEntity foo = spt.Translate&lt;FooEntity&gt;("[dbo].[foo_get]", null).Include(f =&gt; f.Bars).FirstOrDefault();
  }
  </code></pre>