Маршрутизация атрибутов для двух действий приводит к недопустимому шаблону пути OData.

Итак, у меня есть две функции, которые возвращают клиента, который получает два разных параметра. Один из них является идентификатором клиента, а другой — его номером клиента.

Мой контроллер:

using System.Linq;
using System.Net;
using System.Web.Http;
using System.Web.OData;
using System.Web.OData.Routing;
using Models;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using System.Web.OData.Extensions;
using Importing;
using Objects;
using Microsoft.OData;

namespace Controllers
{
    public class CustomersController : ODataController
    {
        // GET: CustomerByCNO(5)
        [HttpGet]
        [ODataRoute("CustomerByCNO({key})")]
        [EnableQuery]
        public SingleResult<CustomerDTO> GetCustomerByCNO([FromODataUri]string key)
        {
            Import i = new Import();

            var customer = i.GetCustomer(key).ProjectTo<CustomerDTO>().AsQueryable();

            return SingleResult.Create(customer);
        }

        // GET: Customer(5)
        [HttpGet]
        [ODataRoute("Customer({id})")]
        [EnableQuery]
        public SingleResult<CustomerDTO> Get([FromODataUri]int id)
        {
            Import i = new Import();

            var customer = i.GetCustomer(id).ProjectTo<CustomerDTO>().AsQueryable();

            return SingleResult.Create(customer);
        }
    }
}

Инициализация:

using AutoMapper;
using Models;
using Objects;
using System.Web.Http;
using System.Web.OData.Builder;
using System.Web.OData.Extensions;
using Microsoft.OData.Edm;

namespace API
{
    public static class WebApiConfig
    {
        public static void ConfigureAPI(HttpConfiguration config)
        {
            config.MapODataServiceRoute(
                routeName: "odata",
                routePrefix: "",
                model: GetEdmModel()
            );

            config.EnsureInitialized();
        }

        private static IEdmModel GetEdmModel()
        {
            ODataConventionModelBuilder builder = new ODataConventionModelBuilder
            {
                Namespace = "Controllers",
                ContainerName = "DefaultContainer"
            };
            builder.EntitySet<CustomerDTO>("Customer")
                .EntityType.HasKey(c => c.Id)
                .CollectionProperty(c => c.CustomFields);

            var edmModel = builder.GetEdmModel();
            return edmModel;
        }
    }
}

В то время как вторые функции работают так, как предполагалось, первые функции не работают, и функция SureInitialized() выдает InvalidOperationException, сообщая, что это не допустимый шаблон пути OData и что ресурс не найден. Как я могу заставить это работать? Не совсем уверен, что мне здесь не хватает.

ОБНОВЛЕНИЕ 1:

Изменение метода контроллера на это:

        [HttpGet]
        [ODataRoute("CustomerByNo(No={no})")]
        public SingleResult<CustomerDTO> CustomerByNo([FromODataUri] int no)
        {
            Import i = new Import();

            var customer = i.GetCustomer(no.ToString()).ProjectTo<CustomerDTO>().AsQueryable();

            return SingleResult.Create(customer);
        }

с этой дополнительной строкой в ​​​​конфигурации:

        builder.Function("CustomerByNo").Returns<SingleResult<CustomerDTO>>().Parameter<int>("No");

Сделано так, чтобы я мог получить доступ к функциям, по крайней мере. Мне также пришлось изменить параметр на int, похоже, он не любит строки? Однако возвращаемое значение не десериализуется и отображается как обычно. Кроме того, если я оставлю строку [EnableQuery] в объявлении метода, вызов завершится сбоем, говоря, что он не знает, как десериализоваться, поскольку, я думаю, он не привязан к набору сущностей Customer.

Однако попытка сделать это таким образом приводит к исходному сообщению об ошибке, что ресурс не найден:

        builder.EntityType<CustomerDTO>().Collection.Function("CustomerByNo").Returns<SingleResult<CustomerDTO>>().Parameter<int>("No");

person Peter    schedule 15.05.2018    source источник


Ответы (1)


Вы должны объявить свои пользовательские функции odata в модели соглашения:

FunctionConfiguration customerByCNOFunction = builder.Function("CustomerByCNO");
customerByCNOFunction.Returns<CustomerDTO>();
customerByCNOFunction.Parameter<string>("key");

Обновлять :

Мой первый ответ был для объявления функций, которые возвращают тип, не запрашиваемый в odata. Чтобы включить запрос, функция должна вернуть объект odata из набора объектов:

builder.Function("CustomerByNo").ReturnsFromEntitySet<CustomerDTO>("Customer").Parameter<int>("No")
person J.Loscos    schedule 15.05.2018
comment
Спасибо! Но это не совсем работает. В ответ добавлен SingleResult CustomerDTO, но это все равно не имеет значения. Это просто дает мне 404. - person Peter; 16.05.2018
comment
Просто чтобы убедиться, что вы используете правильный URL-адрес, URL-адрес пользовательской функции odata: yourhost/CustomerByCNO(key) Имя контроллера отсутствует в URL-адресе - person J.Loscos; 16.05.2018
comment
Да :) Проверьте мое редактирование, может быть, это даст вам больше внутренней информации. - person Peter; 16.05.2018
comment
Попробуйте с builder.Function(CustomerByCNO).ReturnsCollectionFromEntitySet‹CustomerDTO›(Customer).Parameter‹int›(No) Таким образом, он будет знать, что тип результата исходит из entitySet клиентов - person J.Loscos; 16.05.2018
comment
builder.Function(CustomerByNo).ReturnsFromEntitySet‹CustomerDTO›(Customer).Parameter‹int›(No); Работал как шарм. Благодарю вас! :D - person Peter; 16.05.2018
comment
хорошо :) Я обновил свой ответ, включив в него правильное решение - person J.Loscos; 16.05.2018