OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
855 stars 473 forks source link

Filtering by OpenTypes doesn't work with Linq to Entity #487

Open katan4ig opened 9 years ago

katan4ig commented 9 years ago

I store the data for the open types in the database, and I implemented dynamic initialization for them. I'm using the latest version of OData v4 and EF 6.

My model:

public class ExampleDto
{
        private IEnumerable<CustomAttribute> _customAttributes { get; set; }
        public IEnumerable<CustomAttribute> Attributes
        {
            get { return _customAttributes ?? (_customAttributes = new List<CustomAttribute>()); }
            set { _customAttributes = value; }
        }

        private IDictionary<string, object> _dynamicAttributes { get; set; }
        public IDictionary<string, object> DynamicAttributes
        {
            get
            {
                _dynamicAttributes = new Dictionary<string, object>();
                if (Attributes != null && Attributes.Any())
                {
                    _dynamicAttributes = Attributes.ToDictionary(s => s.Name, s => s.Value);
                }
                return _dynamicAttributes;
            }
            set { _dynamicAttributes = value; }
        }
}

"Attributes" field initialization:

var attributes = _exampleRepository.Select(s => new ExampleDto() 
{ 
   Attributes = new CustomAttribute()
   {
     Name = s.Name,
     Value = s.Value 
   }
}

return Ok(attributes)

This works fine, but if I try to use OData filters, Web API to throw next exception: "Unknown LINQ expression of type 'Index'"

This exception thrown only with filtering by open types. Also, this method work if I use ToList() function before applies Odata filters. But i can't use this way because, if used ToLIst() before filtering then i get all data from DB. This is not a good solution for performance.

congysu commented 9 years ago

Can @katan4ig share how the model is defined? and what is the $filter clause?

katan4ig commented 9 years ago

Model defined into Linq To Entity expression, how it described above.

_exampleRepository.Select(s => new ExampleDto()  <= determination new DTO model for each DB item 
{ 
   Attributes = new CustomAttribute()  <= determination Name Value collection for the Open Type
   {
     Name = s.Name,
     Value = s.Value 
   }
}

Property "Attributes" initialized into query. Property "DynamicAttributes" from ExampleDto initialized based on "Attributes" property, when API causes him getter.

Result without $filter condition, for example:

[{
    Attributes:[
         {
                Name : "DynamicField1",
                Value : "1"
         },
         {
                Name : "DynamicField2",
                Value : "2"
          },
          {
                Name : "DynamicField3",
                Value : "3"
          },
          {
               Name : "DynamicField4",
               Value : "4"
          }
     ],
     DynamicField1:"1",
     DynamicField2:"2",
     DynamicField3:"3",
     DynamicField4:"4"
},
{
    Attributes:[
         {
                Name : "DynamicField1",
                Value : "5"
         },
         {
                Name : "DynamicField2",
                Value : "6"
          },
          {
                Name : "DynamicField3",
                Value : "7"
          },
          {
               Name : "DynamicField4",
               Value : "8"
          }
     ],
     DynamicField1:"5",
     DynamicField2:"6",
     DynamicField3:"7",
     DynamicField4:"8"
}]

Api returned objects with all dynamic properties(Open Types).

Filter condition which thrown exception:

$filter=DynamicField1 eq '1'

Let me know if you need more information. Thanks.

congysu commented 9 years ago

What does the $metadata - model - look like? Query options for dynamic properties support in-memory collection, but is yet to support EF.

katan4ig commented 9 years ago

$metadata looks like:

<ComplexType Name="ExampleDto" OpenType="true">
     <Property Name="Attributes" Type="Collection(Common.DTO.Actions.CustomAttribute)" />
</ComplexType>

EF doesn't support dictionaries, maybe a problem in this.

thornpar commented 8 years ago

Im struggling with the same problem. How should you do this?

katan4ig commented 8 years ago

I'm convert collection to list and work with this in-memory. It's bad way but i didn't found another solution.

thornpar commented 8 years ago

"Query options for dynamic properties support in-memory collection, but is yet to support EF."

Are there any plans to support this? We are considering putting collection into memory. But this is really not good!

thornpar commented 8 years ago

@congysu please see my comment above.

thornpar commented 8 years ago

@xuzhg please see comment above