Search code examples
asp.net-web-apiodata

OData "Sequence contains more than one element" error


Sequence contains more than one element at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source) at System.Web.Http.OData.Builder.ODataConventionModelBuilder.RemoveBaseTypeProperties(EntityTypeConfiguration derivedEntity, EntityTypeConfiguration baseEntity) at System.Web.Http.OData.Builder.ODataConventionModelBuilder.DiscoverInheritanceRelationships()...

Here's my binding code:

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<PeopleDto>("People");

Here's my Controller code:

[Queryable]
public IEnumerable<PeopleDto> Get(
        [FromUri] Credentials credentials, 
        ODataQueryOptions<PeopleDto> options, 
        int departmentId,
        DetailLevel detail = DetailLevel.Low)
{
    var count = _repository.Filter(x => x.DepartmentId == departmentId && x.Active);
    options.ApplyTo(count);
    int total = count.Count();

    switch (detail)
    {
        case DetailLevel.Low:
            return new Paginable<PeopleDto>(GetMyPeopleLo(departmentId, options), total);
        // [...]
    }
}

Paginable<T> implements IEnumerable<T>. And the GetMyPeopleLo() (not the real name) method applies the options to the additional queries. (I don't need additional queries anymore, because I have moved the location of the pagination code, but I just haven't refactored that part yet).

Is this one of those cases where there's an issue with the pre-release version of oData?

Update: If I comment out the Queryable attribute, it seems to work, but any filter that I specify in the querystring is not actually applied when options are applied to the count query.


Solution

  • A couple things jump out here:

    • You shouldn't be using [Queryable] and ODataQueryOptions. They're used for doing the same thing. In this case, ODataQueryOptions seems more appropriate so you should just remove [Queryable].
    • You're not using the results of your query. Instead of this

      var count = _repository.Filter(x => x.DepartmentId == departmentId && x.Active);
      options.ApplyTo(count);
      int total = count.Count();
      

      You should be writing this:

      var count = _repository.Filter(x => x.DepartmentId == departmentId && x.Active);
      var queryResults = options.ApplyTo(count) as IQueryable<PeopleDto>;
      int total = queryResults.Count();
      

      That way your total will take into account the OData query options.

    • Consider using PageResult<T> instead of Paginable since the OData formatters will take the count and insert it correctly into the OData feed.