Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embedded $expand not returning the Navigation property in certain scenarios #2076

Open
saidinarts opened this issue Mar 3, 2020 · 3 comments
Assignees
Labels
Milestone

Comments

@saidinarts
Copy link

In a specific scenario, where a [Contained] houses a EntitySet, an embedded navigational property two levels deep within said EntitySet will not be returned.

Assemblies affected

Microsoft.AspNet.OData lib 7.6.0

Reproduce steps

The following entities are defined:

    public class Customer 
    {
        [Key]
        public int ID { get; set; }
        
        [Contained]
        public ICollection<CustomerReferral> CustomerReferrals { get; set; }

        [Contained]
        public ICollection<CustomerPhone> Phones { get; set; }
    }

    public class CustomerReferral
    {
        [Key]
        public int ID { get; set; }

        public int CustomerID { get; set; }

        public int ReferredCustomerID { get; set; }

        [Required]
        [ForeignKey(nameof(CustomerID))]
        public Customer Customer { get; set; }

        [Required]
        [ForeignKey(nameof(ReferredCustomerID))]
        public Customer ReferredCustomer { get; set; }
    }

    public class CustomerPhone
    {
        [Key]
        public int ID { get; set; }

        [Editable(false)]
        public int CustomerID { get; set; }

        [Contained]
        public CustomerPhoneNumberFormatted Formatted { get; set; }
    }

    public class CustomerPhoneNumberFormatted
    {
        [Key]
        public int CustomerPhoneNumberID { get; set; }

        public string FormattedNumber { get; set; }
    }

The DbContext model builder has the following:

            modelBuilder.Entity<CustomerPhone>().HasOptional(a => a.Formatted).WithRequired();
            modelBuilder.Entity<Customer>()
                .HasMany(c => c.CustomerReferrals)
                .WithRequired(c => c.Customer)
                .HasForeignKey(c => c.CustomerID);

The OData model builder is as follows:

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Customer>("Customers");

A defined route is established for CustomerReferrals from a Customer, and the following query is applied:

salesinventory/Customers(486)/CustomerReferrals?$expand=ReferredCustomer($expand=Phones($expand=Formatted))

Expected result

The Formatted entity is returned in the results.

Actual result

The Formatted entity is not returned. Everything is returned except the Formatted entity.

Additional detail

The breakdown seems to happen during the serialization of the OData resource. The actual underlining SQL call and the entities returned to the ODataResourceSerializer have the Formatted class, but it is dropped. This seems to be as the NavigationSource is null on the given ResourceContext for CustomerPhone.

@isabekyan
Copy link

Had the same issue with implicit containment - no attribute, no entity set declaration.
Our workaround is currently having no containment and declare fake entity set for every contained entity type.

@xuzhg
Copy link
Member

xuzhg commented Mar 12, 2020

@saidinarts @isabekyan
Thanks for reporting this. I dig a lot for this scenario, there are two places (wrong) that needs to fix:

  1. at model side (you side), all non-containment navigation property should have a navigation property binding. Please refer Modify to fix issue for #2076, Containment expand problem #2083 sample codes about how to add the navigation property binding.
  2. at ASP.NET Core OData side (our side), the ODataQueryOptionParser should instantiate with the Path, or at lease know the path. Otherwise, the Query option parser can't figure out the correct navigation source to use. The change is also include in Modify to fix issue for #2076, Containment expand problem #2083.

The PR #2083 itself includes the sample project change to use your sample codes. For example i change to use EF from EF Core. FYI. Thanks.

The response for your query (http://localhost:5000/odata/Customers(456)/CustomerReferrals?$expand=ReferredCustomer($expand=Phones($expand=Formatted))) looks like:

{
    "@odata.context": "http://localhost:5000/odata/$metadata#Customers(456)/CustomerReferrals(ReferredCustomer(Phones(Formatted())))",
    "value": [
        {
            "ID": 18,
            "CustomerID": 456,
            "ReferredCustomerID": 8,
            "ReferredCustomer": {
                "ID": 456,
                "Phones": [
                    {
                        "ID": 21,
                        "CustomerID": 456,
                        "Formatted": {
                            "CustomerPhoneNumberID": 17,
                            "FormattedNumber": "abc"
                        }
                    },
                    {
                        "ID": 22,
                        "CustomerID": 457,
                        "Formatted": {
                            "CustomerPhoneNumberID": 18,
                            "FormattedNumber": "abc"
                        }
                    }
                ]
            }
        },
        {
            "ID": 19,
            "CustomerID": 456,
            "ReferredCustomerID": 9,
            "ReferredCustomer": {
                "ID": 456,
                "Phones": [
                    {
                        "ID": 21,
                        "CustomerID": 456,
                        "Formatted": {
                            "CustomerPhoneNumberID": 17,
                            "FormattedNumber": "abc"
                        }
                    },
                    {
                        "ID": 22,
                        "CustomerID": 457,
                        "Formatted": {
                            "CustomerPhoneNumberID": 18,
                            "FormattedNumber": "abc"
                        }
                    }
                ]
            }
        }
    ]
}

@mikepizzo

xuzhg added a commit to xuzhg/WebApi that referenced this issue Mar 12, 2020
@mikepizzo mikepizzo modified the milestones: 7.4, 7.5 May 6, 2020
@xuzhg xuzhg modified the milestones: 7.5, 7.6 Oct 13, 2020
@SireGoatMonkey
Copy link

Can I ask why the commit for this issue hasn't been merged yet? It looks like the fix was identified pretty quickly but this issue has been open for over a year. I still hit this fairly frequently and would love for the fix to be implemented. Thank you!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants