WebApi icon indicating copy to clipboard operation
WebApi copied to clipboard

Dynamic properties with DerivesFrom is not working

Open komdil opened this issue 3 years ago • 2 comments

I am having problem with a dynamic properties. It looks like it is not working with multiple inheritance. OData is not seeing dynamic properties from the base class.

Assemblies affected

Microsoft.AspNetCore.OData 8.0.11

Reproduce steps

Currently my model classes looks like this:

    public class Laptop : Product
    {
        public decimal Price { get; set; }
        public string Manufacturer { get; set; }
        public int Ram { get; set; }
        public string CPU { get; set; }
        public int HardDrive { get; set; }
    }

    public class ProductBase : ServerEntityBase
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    public class Product : ProductBase
    {
        public string Description { get; set; }
    }

    public abstract class ServerEntityBase
    {
        [NotMapped]
        public virtual IDictionary<string, object> Properties { get; set; }

        public ServerEntityBase()
        {

            Properties = new Dictionary<string, object>()
            {
                { "MySuperProperty","MySuperValue" }
            };
        }
    }

And my configuration:

        IEdmModel GetEdmModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
            ProductBaseMapping(builder);
            ProductMapping(builder);
            LaptopMapping(builder);
            return builder.GetEdmModel();
        }

        void ProductBaseMapping(ODataConventionModelBuilder builder)
        {
            builder.EntityType<ProductBase>();
            builder.EntitySet<ProductBase>("ProductBase");
        }

        void ProductMapping(ODataConventionModelBuilder builder)
        {
            builder.EntityType<Product>().DerivesFrom<ProductBase>();
            builder.EntitySet<Product>("Product");
        }

        void LaptopMapping(ODataConventionModelBuilder builder)
        {
            builder.EntityType<Laptop>().DerivesFrom<Product>();
            builder.EntitySet<Laptop>("Laptop");
        }

I am sending request for 3 routes: https://localhost:7258/api/odata/productbase?$select=MySuperProperty https://localhost:7258/api/odata/product?$select=MySuperProperty https://localhost:7258/api/odata/laptop?$select=MySuperProperty

Expected result

I think it should work

Actual result

I am getting error: Could not find a property named 'MySuperProperty' on type [MyType]

komdil avatar Feb 11 '23 12:02 komdil

@komdil Thanks for reporting the issue. I'm investigating what could be going wrong when DerivesFrom method is used. Could you in the meantime just remove the lines with DerivesFrom? The inheritance will be resolved by the model builder even without that. If you remove the line, you should find that all the derived types will be marked with "OpenType"=True attribute in the service metadata and the $select expression will work as expected.

gathogojr avatar Feb 15 '23 12:02 gathogojr

@komdil Thanks for reporting the issue. I'm investigating what could be going wrong when DerivesFrom method is used. Could you in the meantime just remove the lines with DerivesFrom? The inheritance will be resolved by the model builder even without that. If you remove the line, you should find that all the derived types will be marked with "OpenType"=True attribute in the service metadata and the $select expression will work as expected.

You are right. I am using DerivesFrom for making workarounds with my base interface. I have an interface:

   public interface ISuperEntity
    {
        string Name { get; set; }
    }

This is a base interface of all classes. As you know OData does not support interfaces. I have a lot of places that I am using navigation property with that interface type. For example, SuperEntity property in my Laptop class. I am configuring all my classes with DerivesFrom and base class derives from this interface:

builder.EntityType<ServerEntityBase>().DerivesFrom<ISuperEntity>();

Then I can do $expand for this property. When I don;t use DerivesFrom it will show me exception:

Microsoft.OData.ODataException: The type 'ProductAPI.Model.Laptop' of a resource in an expanded link is not compatible with the element type 'ProductAPI.Model.ISuperEntity' of the expanded link. Entries in an expanded link must have entity types that are assignable to the element type of the expanded link.

Is there any difference between resolving inheritance by the model builder and by DerivesFrom? Should they work with the same behavior?

komdil avatar Feb 16 '23 06:02 komdil