odata.net icon indicating copy to clipboard operation
odata.net copied to clipboard

How to do nested $expand?

Open danleydmello opened this issue 3 years ago • 6 comments

I am trying to query an entity using nested expand to get the related entities. e.g queryContainer.Persons.Expand("Address($expand=Location)").ExecuteAsync(); These are cardinality one relationships.

I get the results as expected from the Odata service but the Odata client gives error:

Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100] Unhandled exception rendering component: The Id cannot be computed, since the navigation source 'Location' cannot be resolved to a known entity set from model. Microsoft.OData.ODataException: The Id cannot be computed, since the navigation source 'Asset' cannot be resolved to a known entity set from model. at Microsoft.OData.Evaluation.ODataConventionalIdMetadataBuilder.ComputeAndCacheId() at Microsoft.OData.Evaluation.ODataConventionalIdMetadataBuilder.get_ComputedId() at Microsoft.OData.Evaluation.ODataConventionalIdMetadataBuilder.GetId() at Microsoft.OData.ODataResourceBase.get_Id() at Microsoft.OData.Client.Materialization.MaterializerEntry.UpdateEntityDescriptor() at Microsoft.OData.Client.Materialization.FeedAndEntryMaterializerAdapter.ReadEntryCore()

I know the Location is in the model because if I try this query, odata client works: e.g queryContainer.Address.Expand("Location").ExecuteAsync();

danleydmello avatar Oct 01 '21 18:10 danleydmello

It works when this.MergeOption = MergeOption.NoTracking; This is okay for me as my application is Readonly and I don't need tracking on.

danleydmello avatar Oct 04 '21 15:10 danleydmello

I have looked at this issue

Model

public class Book
{
    public int Id { get; set; }
    public string Isbn { get; set; }
    public string Title { get; set; }
    public ICollection<Author> Authors { get; set; }
}

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Award> Awards { get; set; }
}

public class Award
{
    public int Id { get; set; }
    public string Name { get; set; }
}

OData Client Code

string uri = "";
Uri serviceUri = new Uri(uri);
Container dsc = new Container(serviceUri);
var books = await dsc.Books.Expand("Authors($expand=Awards)").ExecuteAsync();
foreach (Book book in books)
{
    Console.WriteLine($"Book: {book.Title}");
    foreach (Author author in book.Authors)
    {
        Console.WriteLine($"\tAuthor: {author.Name}");
        foreach (Award award in author.Awards)
        {
            Console.WriteLine($"\t\t Award: {award.Name}");
        }
    }
}

I do not need to set the MergeOption.NoTracking

Below is my output. FYI am using an In-Memory data store. nestedexpand t

KenitoInc avatar Oct 07 '21 08:10 KenitoInc

@danleydmello Are you using DataServiceCollection?

KenitoInc avatar Oct 07 '21 11:10 KenitoInc

@KenitoInc , In my case, the references are cardinality one , not sure if it matters

public class ActionPlan
    {
        public Guid UniversalID { get; set; }
        
        public MapFailureMode FailureMode { get; set; }
    }

public class MapFailureMode
    {
        public Guid UniversalID { get; set; }            
        public Asset Asset { get; set; }
    }

 public class Asset
    {
        public Guid UniversalID { get; set; }
        public string AssetNumber { get; set; }
        public string Title { get; set; }
    }

QueryContainer Definition, I followed microsoft documentation [https://docs.microsoft.com/en-us/odata/client/using-blazor-wasm-with-odata-client]:

public class QueryContainer : DataServiceContext
   {
       private readonly string _authHeader;

       public QueryContainer(Uri baseUri, string authHeader, IEdmModel edmModel) :base(baseUri)
       {
           _authHeader = authHeader;
           Format.LoadServiceModel = () => edmModel;
           HttpRequestTransportMode = HttpRequestTransportMode.HttpClient;
           Format.UseJson();
           MergeOption = MergeOption.NoTracking;
           ActionPlans = base.CreateQuery<ActionPlan>("MNT/MaintenanceActionPlans");
           SendingRequest2 += QueryContainer_SendingRequest2;
       }

       private void QueryContainer_SendingRequest2(object? sender, SendingRequest2EventArgs e)
       {
           e.RequestMessage.SetHeader("Authorization", _authHeader);
       }

       public DataServiceQuery<ActionPlan> ActionPlans { get; }
   }

danleydmello avatar Oct 07 '21 13:10 danleydmello

Any news on this issue? I really don't like adding MergeOption.NoTracking;

habex-ch avatar Jan 03 '23 17:01 habex-ch