title: OData containment with multiple expand levels not working
body: <p>Software used:</p>
  
  <ul>
  <li>ASP.NET Web API 2.2</li>
  <li>OData v4.0</li>
  <li>OData Client for .NET (v 6.11) </li>
  </ul>
  
  <p>Consider the following model:</p>
  
  <blockquote>
    <p>User (UserId, Name) </p>
    
    <p>UserMessage (UserMessageId, User, Message, DateRead) </p>
    
    <p>Message(MessageId, MessageType, Title, Text) </p>
    
    <p>MessageType (MessageTypeId, Name)</p>
  </blockquote>
  
  <p>An User has many UserMessages and an UserMessage connects an User and a Message and has some extra properties. A Message has a MessageType property which also has some properties.</p>
  
  <p>And the following OData configuration:</p>
  
  <pre><code>var builder = new ODataConventionModelBuilder();
  
  builder.EntitySet&lt;User&gt;("Users");
  // Configure an containment for messages.
  builder.EntityType&lt;User&gt;().ContainsMany(u =&gt; u.Messages);
  builder.EntityType&lt;UserMessage&gt;()
  builder.EntitySet&lt;Message&gt;("Messages");
  builder.EntitySet&lt;MessageType&gt;("MessageTypes");
  
  var edmModel = builder.GetEdmModel();
  
  config.MapODataServiceRoute(
          routeName: "ODataRoute",
          routePrefix: null,
          model: edmModel);
  </code></pre>
  
  <p>Remarks:</p>
  
  <ol>
  <li>Message and MessageType need to be configured as entity sets because we need to be able to add indiviual messages and message types using their corresponding controllers.</li>
  <li>UserMessages are not exposed as a entity set but as a containment on the entity set "Users".</li>
  </ol>
  
  <p>In the "UsersController" the following method is defined to handle the containment for Messages:</p>
  
  <pre><code>[EnableQuery]       
  public async Task&lt;IQueryable&lt;UserMessage&gt;&gt; GetMessages(int key)
  </code></pre>
  
  <p>When selecting all user messages using the containment everything works as expected:</p>
  
  <p>/Users(1)/messages</p>
  
  <p>Returns:</p>
  
  <pre><code>{
      "@odata.context":"https://host/$metadata#Users(1)/messages","value":[
      {
          "userId":1,"messageId":50,"dateRead":null,"userMessageId":481
      }]
  }
  </code></pre>
  
  <p>And an expand on message also works:</p>
  
  <p>/Users(1)/messages?$expand=message</p>
  
  <p>Returns:</p>
  
  <pre><code>{
    "@odata.context":"https://host/$metadata#Users(1)/messages","value":[
      {
        "userId":1,"messageId":50,"dateRead":null,"userMessageId":481,"message":{
          "title":"Test","text":"Test","messageTypeId":1,"messageId":50
        }
      }
    ]
  }
  </code></pre>
  
  <p>However, when expanding MessageType on Message also, the request fails on deserialization on the client (using the OData Client for .NET v6.11). The returned JSON is:</p>
  
  <pre><code>{
    "@odata.context":"https://host/$metadata#Users(1)/messages","value":[
      {
        "userId":1,"messageId":50,"dateRead":null,"userMessageId":481,"message":{
          "title":"Test","text":"Test","messageTypeId":1,"messageId":50,"messageType":{
            "@odata.type":"#Domain.Message.MessageType","name":"General","messageTypeId":1
          }
        }
      }
    ]
  }
  </code></pre>
  
  <p>The error on the client is:</p>
  
  <blockquote>
    <p>When writing a JSON response, a user model must be specified and the
    entity set and entity type must be passed to the
    ODataMessageWriter.CreateODataEntryWriter method or the
    ODataFeedAndEntrySerializationInfo must be set on the ODataEntry or
    ODataFeed that is being written.</p>
  </blockquote>
  
  <p>And the stacktrace:</p>
  
  <blockquote>
    <p>[ODataException: When writing a JSON response, a user model must be specified and the entity set and entity type must be passed to the ODataMessageWriter.CreateODataEntryWriter method or the ODataFeedAndEntrySerializationInfo must be set on the ODataEntry or ODataFeed that is being written.]
    Microsoft.OData.Core.ODataFeedAndEntryTypeContext.ValidateAndReturn(T value) +74
    Microsoft.OData.Core.Evaluation.ODataConventionalEntityMetadataBuilder.ComputeId() +71
    Microsoft.OData.Core.Evaluation.ODataConventionalEntityMetadataBuilder.get_ComputedId() +136
    Microsoft.OData.Client.Materialization.MaterializerEntry.UpdateEntityDescriptor() +426
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadEntryCore() +310
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.TryReadFeedOrEntry(Boolean lazy, ODataFeed&amp; feed, MaterializerEntry&amp; entry) +149
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadNavigationLink() +91
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadEntryCore() +256
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.TryReadFeedOrEntry(Boolean lazy, ODataFeed&amp; feed, MaterializerEntry&amp; entry) +149
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadNavigationLink() +91
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadEntryCore() +256
    Microsoft.OData.Client.Materialization.d__0.MoveNext() +139
    Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.Read() +68
    Microsoft.OData.Client.Materialization.ODataEntityMaterializer.ReadImplementation() +113
    Microsoft.OData.Client.MaterializeAtom.MoveNextInternal() +475
    Microsoft.OData.Client.MaterializeAtom.MoveNext() +108
    System.Linq.d__b1<code>1.MoveNext() +296
    System.Linq.WhereSelectEnumerableIterator</code>2.MoveNext() +165
    System.Collections.Generic.List<code>1..ctor(IEnumerable</code>1 collection) +536
    System.Linq.Enumerable.ToList(IEnumerable`1 source) +80</p>
  </blockquote>
  
  <p>When requesting the full metadata information for the url using "odata.metadata=full" the server returns the same error as the client:</p>
  
  <pre><code>HTTP/1.1 500 Internal Server Error
  Content-Length: 3455
  Content-Type: application/json; odata.metadata=full; charset=utf-8
  Server: Microsoft-IIS/7.5
  OData-Version: 4.0
  X-Powered-By: ASP.NET
  Date: Tue, 23 Jun 2015 09:47:20 GMT
  
  {
    "error":{
      "code":"","message":"An error has occurred.","innererror":{
        "message":"When writing a JSON response, a user model must be specified and the entity set and entity type must be passed to the ODataMessageWriter.CreateODataEntryWriter method or the ODataFeedAndEntrySerializationInfo must be set on the ODataEntry or ODataFeed that is being written.","type":"Microsoft.OData.Core.ODataException","stacktrace":"   at Microsoft.OData.Core.ODataFeedAndEntryTypeContext.ValidateAndReturn[T](T value)\r\n   at Microsoft.OData.Core.Evaluation.ODataConventionalEntityMetadataBuilder.ComputeId()\r\n   at Microsoft.OData.Core.Evaluation.ODataConventionalEntityMetadataBuilder.get_ComputedId()\r\n   at Microsoft.OData.Core.Evaluation.ODataConventionalEntityMetadataBuilder.TryGetIdForSerialization(Uri&amp; id)\r\n   at Microsoft.OData.Core.JsonLight.ODataJsonLightEntryAndFeedSerializer.WriteEntryStartMetadataProperties(IODataJsonLightWriterEntryState entryState)\r\n   at Microsoft.OData.Core.JsonLight.ODataJsonLightWriter.StartEntry(ODataEntry entry)\r\n   at Microsoft.OData.Core.ODataWriterCore.InterceptException(Action action)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperty(KeyValuePair`2 navigationPropertyToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperties(IDictionary`2 navigationPropertiesToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperty(KeyValuePair`2 navigationPropertyToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteExpandedNavigationProperties(IDictionary`2 navigationPropertiesToExpand, EntityInstanceContext entityInstanceContext, ODataWriter writer)\r\n   at System.Web.OData.Formatter.Serialization.ODataEntityTypeSerializer.WriteEntry(Object graph, ODataWriter writer, ODataSerializerContext writeContext)\r\n   at System.Web.OData.Formatter.Serialization.ODataFeedSerializer.WriteFeed(IEnumerable enumerable, IEdmTypeReference feedType, ODataWriter writer, ODataSerializerContext writeContext)\r\n   at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\r\n   at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Owin.HttpMessageHandlerAdapter.&lt;BufferResponseContentAsync&gt;d__13.MoveNext()"
      }
    }
  }
  </code></pre>
  
  <p>We need to expand the messageType on the message because we need the data on the client.</p>
  
  <p>Any suggestions?</p>