EntitySetController $expand and $select not working

asp.net-web-api, c#, odata

Solution

If you want to use your own attribute you need to register it!

Just call `config.EnableQuerySupport(new ControlledQueryableAttribute());`

Problem

I'm trying to get pages of results for a table, using `OData` provided by an `EntitySetController`. However all of my requests for `$select` and `$expand` fail. Any ideas? I'm using Entity Framework v6 and System.Web.Http.OData v5. My entity: ``` public partial class Contact : BaseEntity { [Column("cont_id"), Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Column("cust_id"), EitherRequired("SupplierId", Exclusive = true)] public int? CustomerId { get; set; } public virtual Customer Customer { get; set; } [Column("supp_id")] public int? SupplierId { get; set; } public virtual Supplier Supplier { get; set; } [Column("cont_title"), Required(AllowEmptyStrings = true), StringLength(5)] public string Title { get; set; } [Column("cont_firstname"), Required(AllowEmptyStrings = true), StringLength(50)] public string FirstName { get; set; } [Column("cont_lastname"), Display(Name = "Last Name"), Required(AllowEmptyStrings = true), StringLength(50)] public string LastName { get; set; } [Column("cont_phone"), DataType(DataType.PhoneNumber), StringLength(20)] public string PhoneNumber { get; set; } } ``` My Controller: ``` public abstract class RepositoryController<TEntity, TKey> : EntitySetController<TEntity, TKey> where TEntity : BaseEntity { protected readonly IRepository<TEntity> repository; protected readonly Func<TEntity, TKey> keySelector; public RepositoryController(IRepository<TEntity> repository, Func<TEntity, TKey> keySelector) : base() { this.repository = repository; this.keySelector = keySelector; } // GET api/entity [HttpGet] [Queryable] public override IQueryable<TEntity> Get() { return repository.Get(); } public override void Delete(TKey key) { repository.Delete(GetEntityByKey(key)); UnitOfWork.Save(); } protected override TEntity GetEntityByKey(TKey key) { return repository.Get(key); } protected override TKey GetKey(TEntity entity) { return keySelector(entity); } protected override TEntity CreateEntity(TEntity entity) { if (!ModelState.IsValid) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } var result = repository.Insert(entity); UnitOfWork.Save(); return result; } protected override TEntity UpdateEntity(TKey key, TEntity update) { if (!ModelState.IsValid) { throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState)); } repository.Update(update, key); UnitOfWork.Save(); return update; } } ``` My request: `http://localhost:60642/odata/Contact?$inlinecount=allpages&$top=20&$expand=Customer,Supplier` The result: ``` { "odata.error":{ "code":"","message":{ "lang":"en-US","value":"The query specified in the URI is not valid." },"innererror":{ "message":"Could not find a property named 'Customer' on type 'System.Web.Http.OData.Query.Expressions.SelectAllAndExpand_1OfContact'.","type":"Microsoft.Data.OData.ODataException","stacktrace":" at Microsoft.Data.OData.Query.SyntacticAst.ExpandBinder.GenerateExpandItem(ExpandTermToken tokenIn)\r\n at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()\r\n at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()\r\n at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)\r\n at Microsoft.Data.OData.Query.SyntacticAst.ExpandBinder.Bind(ExpandToken tokenIn)\r\n at Microsoft.Data.OData.Query.SelectExpandSemanticBinder.Parse(IEdmEntityType elementType, IEdmEntitySet entitySet, ExpandToken expandToken, SelectToken selectToken, ODataUriParserConfiguration configuration)\r\n at Microsoft.Data.OData.Query.ODataUriParser.ParseSelectAndExpandImplementation(String select, String expand, IEdmEntityType elementType, IEdmEntitySet entitySet)\r\n at Microsoft.Data.OData.Query.ODataUriParser.ParseSelectAndExpand(String select, String expand, IEdmEntityType elementType, IEdmEntitySet entitySet)\r\n at System.Web.Http.OData.Query.SelectExpandQueryOption.get_SelectExpandClause()\r\n at System.Web.Http.OData.Query.Validators.SelectExpandQueryValidator.Validate(SelectExpandQueryOption selectExpandQueryOption, ODataValidationSettings validationSettings)\r\n at System.Web.Http.OData.Query.SelectExpandQueryOption.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.Http.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n at System.Web.Http.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.Http.QueryableAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)\r\n at System.Web.Http.QueryableAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n at System.Web.Http.QueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)" } } } ``` But I see it on `$metadata`: ``` <EntityType Name="Contact"> <Key> <PropertyRef Name="Id"/> </Key> <Property Name="Id" Type="Edm.Int32" Nullable="false"/> <Property Name="CustomerId" Type="Edm.Int32"/> <Property Name="SupplierId" Type="Edm.Int32"/> <Property Name="Title" Type="Edm.String" Nullable="false"/> <Property Name="FirstName" Type="Edm.String" Nullable="false"/> <Property Name="LastName" Type="Edm.String" Nullable="false"/> <Property Name="PhoneNumber" Type="Edm.String"/> <NavigationProperty Name="Customer" Relationship="Vantage.Data.Core.Entities.Vantage_Data_Core_Entities_Contact_Customer_Vantage_Data_Core_Entities_Customer_CustomerPartner" ToRole="Customer" FromRole="CustomerPartner"/> <NavigationProperty Name="Supplier" Relationship="Vantage.Data.Core.Entities.Vantage_Data_Core_Entities_Contact_Supplier_Vantage_Data_Core_Entities_Supplier_SupplierPartner" ToRole="Supplier" FromRole="SupplierPartner"/> </EntityType> ``` I get a similar exception for selects. What' wrong? Edit: Update. I get a different error if I try to filter something on the root entity while also expanding. Seems the entity truly is screwed. `http://localhost:60642/odata/Contact?$top=20&$inlinecount=allpages&$filter=substringof('ORDERS',%20LastName)%20eq%20true&$expand=Customer,Supplier` ``` { "odata.error":{ "code":"","message":{ "lang":"en-US","value":"The query specified in the URI is not valid. Could not find a property named 'LastName' on type 'System.Web.Http.OData.Query.Expressions.SelectAllAndExpand_1OfContact'." },"innererror":{ "message":"Could not find a property named 'LastName' on type 'System.Web.Http.OData.Query.Expressions.SelectAllAndExpand_1OfContact'.","type":"Microsoft.Data.OData.ODataException","stacktrace":" at Microsoft.Data.OData.Query.EndPathBinder.GeneratePropertyAccessQueryForOpenType(EndPathToken endPathToken, SingleValueNode parentNode)\r\n at Microsoft.Data.OData.Query.EndPathBinder.BindEndPath(EndPathToken endPathToken, BindingState state)\r\n at Microsoft.Data.OData.Query.MetadataBinder.BindEndPath(EndPathToken endPathToken)\r\n at Microsoft.Data.OData.Query.MetadataBinder.Bind(QueryToken token)\r\n at Microsoft.Data.OData.Query.MetadataBinder.BindFunctionParameter(FunctionParameterToken token)\r\n at Microsoft.Data.OData.Query.MetadataBinder.Bind(QueryToken token)\r\n at Microsoft.Data.OData.Query.FunctionCallBinder.<BindFunctionCall>b__6(FunctionParameterToken ar)\r\n at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()\r\n at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)\r\n at Microsoft.Data.OData.Query.FunctionCallBinder.BindFunctionCall(FunctionCallToken functionCallToken, BindingState state)\r\n at Microsoft.Data.OData.Query.MetadataBinder.BindFunctionCall(FunctionCallToken functionCallToken)\r\n at Microsoft.Data.OData.Query.MetadataBinder.Bind(QueryToken token)\r\n at Microsoft.Data.OData.Query.BinaryOperatorBinder.GetOperandFromToken(BinaryOperatorKind operatorKind, QueryToken queryToken)\r\n at Microsoft.Data.OData.Query.BinaryOperatorBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n at Microsoft.Data.OData.Query.MetadataBinder.BindBinaryOperator(BinaryOperatorToken binaryOperatorToken)\r\n at Microsoft.Data.OData.Query.MetadataBinder.Bind(QueryToken token)\r\n at Microsoft.Data.OData.Query.FilterBinder.BindFilter(QueryToken filter)\r\n at Microsoft.Data.OData.Query.ODataUriParser.ParseFilterImplementation(String filter, IEdmType elementType, IEdmEntitySet entitySet)\r\n at System.Web.Http.OData.Query.FilterQueryOption.get_FilterClause()\r\n at System.Web.Http.OData.Query.Validators.FilterQueryValidator.Validate(FilterQueryOption filterQueryOption, ODataValidationSettings settings)\r\n at System.Web.Http.OData.Query.FilterQueryOption.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.Http.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n at System.Web.Http.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.Http.QueryableAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)\r\n at System.Web.Http.QueryableAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n at System.Web.Http.QueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)" } } } ```

Original source