1

Closed

OData 2: $filter=round(FloatProperty) eq 1 halt with ArgumentException during parsing

description

I have an entity with following property:
public float FloatProperty { get; set; }
When I try to query data from it using my OData contoller like
http://localhost/WebApi/EntityWithSimpleTypes?$filter=round(FloatProperty) eq 1
I get an exception.
It seems the float argument is converted to Nullable<double> to make a call to Round method there. But there are two Round methods and wrong one is selected (i.e. Round(decimal))
I suppose the float argument should be converted to double or proper methods need to be selected for nullable type

file attachments

Closed Feb 25 at 9:24 PM by trdai
close as duplicate to 1580

comments

raghuramn wrote Nov 8, 2013 at 5:03 PM

Round according to the OData spec is defined only on Edm.Double and Edm.Decimal. It is not defined on Edm.Single which float maps to and hence the cast.

http://www.odata.org/documentation/odata-v3-documentation/url-conventions/#512423_round

NonExisto wrote Nov 9, 2013 at 2:14 AM

The cast is OK, no argue at all. But it's result here is not OK.

jacalvar wrote Feb 4 at 10:29 PM

No repro on Owin Self Host in 5.1
class Program
{
    private static readonly string serverUrl = "http://localhost:12345";
    static void Main(string[] args)
    {
        using (WebApp.Start(serverUrl, Configuration))
        {
            Console.WriteLine("Server listening on {0}", serverUrl);
            Console.Read();
        }
    }

    private static void Configuration(IAppBuilder builder)
    {
        ODataModelBuilder omb = new ODataConventionModelBuilder();
        omb.EntitySet<SampleType>("SampleTypes");

        HttpConfiguration configuration = new HttpConfiguration();
        configuration.Routes.MapODataRoute("odata", "odata", omb.GetEdmModel());

        builder.UseWebApi(configuration);
    }
}

public class SampleTypesController : ODataController
{
    [Queryable]
    public IHttpActionResult Get()
    {
        return Ok(Enumerable.Range(0, 10).Select(i => new SampleType
        {
            Id = i,
            FloatProp = 0.6f + i
        }));
    }
}

public class SampleType
{
    public int Id { get; set; }
    public float FloatProp { get; set; }
}

NonExisto wrote Feb 5 at 5:12 AM

OK lets close this one. I tried to create my own simple case scenario to reproduce the issue but without any success so far. It seems to be some sort of problem with main project setup. Thanks.

NonExisto wrote Feb 5 at 5:35 AM

Nah, it was nullable float property. Here is a test project for you.

NonExisto wrote Feb 5 at 6:01 AM

Here is some code to observe for entity and controller:
public class Entity
{
    public int Key { get; set; }
    public float? Data { get; set; }
}

public class EntitiesController : EntitySetController<Entity, int>
{
    [Queryable]
    public override IQueryable<Entity> Get()
    {
        return
            new[] {new Entity {Key = 1, Data = 6.2f}, new Entity {Key = 2, Data = -3.12f}, new Entity {Key = 3, Data = 0f}}
                .AsQueryable();
    }

    protected override Entity GetEntityByKey(int key)
    {
        return new Entity {Key = key, Data = 6.25f};
    }
}
The registration is simple as well:
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var modelBuilder = new ODataConventionModelBuilder();
        EntityTypeConfiguration configuration = modelBuilder.AddEntity(typeof(Entity));
        configuration.HasKey(typeof(Entity).GetProperty("Key"));
        
        modelBuilder.AddEntitySet("Entities", configuration);
        
        config.Routes.MapODataRoute("WebApi", "WebApi", modelBuilder.GetEdmModel());
            
    }
}
To test that I have a separate project with certain infrastructure:
[TestClass]
public class EntitiesControllerTest
{
    private HttpServer _server;
    private HttpClient _client;
    private const string DefaultContentType = "application/atom+xml";
    private const string JsonContentType = "application/json";
    private const string Value = "value";
        
    [TestInitialize]
    public void Setup()
    {
        HttpConfiguration httpConfiguration = new HttpConfiguration();
        _server = new HttpServer(httpConfiguration);
        _client = new HttpClient(_server);
        httpConfiguration.EnsureInitialized();
        WebApiConfig.Register(httpConfiguration);
    }

    private JObject ReadResponseAsJson(string uri)
    {
        HttpResponseMessage httpResponseMessage = SendRequest(uri, JsonContentType);
        httpResponseMessage.EnsureSuccessStatusCode();
        var readResponseAsJson = ReadResponseAsJson(httpResponseMessage);
        if (readResponseAsJson != null) return readResponseAsJson;
        throw new InvalidOperationException("Failed to retrieve json from response");
    }

    private JObject ReadResponseAsJson(HttpResponseMessage httpResponseMessage)
    {
        ObjectContent content = httpResponseMessage.Content as ObjectContent;

        if (content != null)
        {
            var readAsStringAsync = content.ReadAsStringAsync().Result;
            return JObject.Parse(readAsStringAsync);
        }

        return null;
    }

    protected virtual HttpResponseMessage SendRequest(string uri, string acceptMediaType = DefaultContentType, HttpContent content = null, HttpMethod method = null, string acceptMediaParameter = null)
    {
        HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method ?? HttpMethod.Get, uri);
        if (acceptMediaType != null)
        {
            MediaTypeWithQualityHeaderValue headerValue = new MediaTypeWithQualityHeaderValue(acceptMediaType);
            if (acceptMediaParameter != null)
            {
                headerValue.Parameters.Add(new NameValueHeaderValue(acceptMediaParameter));
            }
            httpRequestMessage.Headers.Accept.Add(headerValue);
        }
        httpRequestMessage.Content = content ?? new StringContent(string.Empty);


        HttpResponseMessage response = _client.SendAsync(httpRequestMessage).Result;

        return response;
    }

        

    [TestMethod]
    public void ODataApiShouldReturnMetadata()
    {
        JObject jo = ReadResponseAsJson("http://localhost/WebApi");
        jo[Value].Count().Should().Be(1);
        jo[Value][0].Value<string>("url").Should().Be("Entities");
    }
        

    [TestMethod]
    public void ODataApiShouldReturnEmptyResultSet()
    {
        JObject jo = ReadResponseAsJson("http://localhost/WebApi/Entities");
        jo[Value].Count().Should().Be(3);
    }

    [TestMethod]
    public void ODataShouldNotFailOnRoungFloatProperty()
    {
        JObject jo = ReadResponseAsJson("http://localhost/WebApi/Entities?$filter=round(Data) eq 6f");
        jo[Value].Count().Should().Be(1);
    }


    [TestMethod]
    public void ODataShouldNotFailOnCeilingFloatProperty()
    {
        JObject jo = ReadResponseAsJson("http://localhost/WebApi/Entities?$filter=ceiling(Data) eq 7f");
        jo[Value].Count().Should().Be(1);
    }

    [TestMethod]
    public void ODataShouldNotFailOnFloorFloatProperty()
    {
        JObject jo = ReadResponseAsJson("http://localhost/WebApi/Entities?$filter=floor(Data) eq 6f");
        jo[Value].Count().Should().Be(1);
    }
}
It uses Fluent Assertions for asserts. MVC5.1 and WebApi2.1 are the same

cysu wrote Feb 17 at 5:12 AM

Seems a duplicate of 1580.

NonExisto wrote Feb 17 at 6:11 AM

yep, a duplicate