Web Api with OData v4 throwing exception on $select

让人想犯罪 __ 提交于 2020-01-13 03:23:26

问题


I'm using the latest version of WebApi and OData and everything is set up to work right. The only problem is when I try to use $select .

It throws the error bellow

  Object of type 'System.Linq.EnumerableQuery`1[System.Web.OData.Query.Expressions.SelectExpandBinder+SelectAll`1[WebApplication1.Controllers.Person]]' cannot be converted to type 'System.Collections.Generic.IEnumerable`1[WebApplication1.Controllers.Person]'.

I looked at the documentation and their suggestion is to use [Queryable] on top of the Get method in the controller or the in WebApiConfig to use config.EnableQuerySupport and neither of these are available options. I'm currently using [EnableQuery]

EDIT

OdataController:

    using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.OData;
using System.Xml.Serialization;

namespace WebApplication1.Controllers
{
    public class PeopleController : ODataController
    {
        // GET api/values
        [EnableQuery]
        public IQueryable<Person> Get()
        {
            return new Person[] { new Person()
            {
                Id = 1,
                FirstName = "Testing",
                LastName = "2"
            },  new Person()
            {
                Id = 2,
                FirstName = "TestTest",
                LastName = "3"
            } }.AsQueryable();
        }

        // GET api/values/5
        public Person Get(int id)
        {
            return new Person()
            {
                Id = 3,
                FirstName = "Test",
                LastName = "1"
            };
        }

        // POST api/values
        public void Post([FromBody]Person value)
        {
        }

        // PUT api/values/5
        public void Put(int id, [FromBody]Person value)
        {
        }

        // DELETE api/values/5
        public void Delete(int id)
        {
        }
    }

    public class Person
    {
        [Key]
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

WebApiConfig

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using System.Web.OData;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using System.Web.OData.Formatter;
using WebApplication1.Controllers;

namespace WebApplication1
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services

            // Web API routes
            config.MapHttpAttributeRoutes();

            var odataFormatters = ODataMediaTypeFormatters.Create();
            config.Formatters.InsertRange(0, odataFormatters);

            ODataModelBuilder builder = new ODataConventionModelBuilder();
            builder.EntitySet<Person>("People");
            config.AddODataQueryFilter();
            config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: "api",
            model: builder.GetEdmModel());

        }
    }
}

UPDATE 2

seems to only throw an error retrieving the data in xml format. Json seems to work


回答1:


This is a known limitation of the XmlMediaTypeFormatter class from the System.Net.Formatting Nuget package. The implementation of the JSON formatter does support the $select and $expand commands but these are not available when content negotiation determines that XML should be returned.

You should look into implementing OData endpoints (as opposed to WebAPI endpoints) should you need to return XML formatted responses. More information on how this can be done can be found here:

http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options




回答2:


Found a solution. It isn't perfect but it does work!

Maybe it will be useful for someone because I've spent on it few hours of research and trying.

Step #1 create custom xml formatter:

public class CustomXmlFormatter : MediaTypeFormatter
{
    private JsonMediaTypeFormatter jFormatter = null;
    public CustomXmlFormatter(JsonMediaTypeFormatter jFormatter)
    {
        SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/xml"));
        this.jFormatter = jFormatter;
    }

    public override bool CanReadType(Type type)
    {
        return false;
    }

    public override bool CanWriteType(Type type)
    {
        return true;
    }
    public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
    {
        return Task.Factory.StartNew(() =>
        {
            using (MemoryStream ms = new MemoryStream())
            {
                var tsk = jFormatter.WriteToStreamAsync(type, value, ms, content, transportContext);
                tsk.Wait();
                ms.Flush();
                ms.Seek(0, SeekOrigin.Begin);

                var xDoc = XDocument.Load(JsonReaderWriterFactory.CreateJsonReader(ms, new XmlDictionaryReaderQuotas()));

                using (XmlWriter xw = XmlWriter.Create(writeStream))
                {
                    xDoc.WriteTo(xw);
                }

            }
        });
    }
}

Step #2 register it in startup section:

        var formatters = ODataMediaTypeFormatters.Create();
        var jsonFormatter = config.Formatters.JsonFormatter;
        var customXmlFormatter = new CustomXmlFormatter(jsonFormatter);

        customXmlFormatter.AddQueryStringMapping("$format", "cxml", "application/xml");
        config.Formatters.Add(customXmlFormatter);

use it as http://url..../actionName?$format=cxml&$select=ObjectName,ObjectId



来源:https://stackoverflow.com/questions/27925062/web-api-with-odata-v4-throwing-exception-on-select

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!