Как передать функцию Array в OData в реализации веб-API ASP.NET?
В спецификации OData V4 указано, что она ДОЛЖНА быть возможной:
https://issues.oasis-open.org/browse/ODATA-636.
"Сложные типы и массивы могут передаваться только через функции Параметрические псевдонимы"
Когда я пытаюсь передать массив с псевдонимами параметров OData, возникает исключение.
/TestEntities/NS.TestFunction(ArrayHere = @р)? @Р = [1,2,3]
Результаты в:
Невозможно применить объект типа "EdmValidCoreModelPrimitiveType" к типу "Microsoft.OData.Edm.IEdmStructuredType
Интересно, что документ метаданных правильно составлен для таких случаев:
<Function Name="TestFunction" IsBound="true">
<Parameter Name="bindingParameter" Type="Collection(NS.TestEntity)"/>
<Parameter Name="ArrayHere" Type="System.Int32[]"/>
<ReturnType Type="Collection(NS.TestEntity)"/>
</Function>
Возможно ли с помощью ASP.NET MVC Web API 2 OData передать массив функции OData в строку запроса?
ОБНОВЛЕНИЕ:
Вот код для создания модели EDM и контроллера.
var builder = new ODataConventionModelBuilder();
builder.Namespace = "NS";
builder.EntitySet<TestEntity>("TestEntities");
builder.EntityType<TestEntity>().Collection
.Function("TestFunction")
.ReturnsCollectionFromEntitySet<TestEntity>("TestEntities")
.Parameter<int[]>("ArrayHere");
Контроллер:
public class TestEntitiesController : ODataController
{
public IEnumerable<TestEntity> TestFunction(int[] arrayHere)
{
throw new NotImplementedException();
}
}
Параметр маркировки с [FromODataUri]
не решает проблему.
ОБНОВЛЕНИЕ 2:
Вот трассировка стека:
at Microsoft.OData.Core.UriParser.TypePromotionUtils.CanConvertTo(SingleValueNode sourceNodeOrNull, IEdmTypeReference sourceReference, IEdmTypeReference targetReference)
at Microsoft.OData.Core.UriParser.Parsers.MetadataBindingUtils.ConvertToTypeIfNeeded(SingleValueNode source, IEdmTypeReference targetTypeReference)
at Microsoft.OData.Core.UriParser.Parsers.FunctionCallBinder.BindSegmentParameters(ODataUriParserConfiguration configuration, IEdmOperation functionOrOpertion, ICollection`1 segmentParameterTokens)
at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryBindingParametersAndMatchingOperation(String identifier, String parenthesisExpression, IEdmType bindingType, ODataUriParserConfiguration configuration, ICollection`1& boundParameters, IEdmOperation& matchingOperation)
at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryCreateSegmentForOperation(ODataPathSegment previousSegment, String identifier, String parenthesisExpression)
at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.CreateNextSegment(String text)
at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.ParsePath(ICollection`1 segments)
at Microsoft.OData.Core.UriParser.Parsers.ODataPathFactory.BindPath(ICollection`1 segments, ODataUriParserConfiguration configuration)
at Microsoft.OData.Core.UriParser.ODataUriParser.ParsePathImplementation()
at Microsoft.OData.Core.UriParser.ODataUriParser.Initialize()
at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath, Boolean enableUriTemplateParsing)
at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath)
at System.Web.OData.Routing.ODataPathRouteConstraint.Match(HttpRequestMessage request, IHttpRoute route, String parameterName, IDictionary`2 values, HttpRouteDirection routeDirection)
at System.Web.Http.Routing.HttpRoute.ProcessConstraint(HttpRequestMessage request, Object constraint, String parameterName, HttpRouteValueDictionary values, HttpRouteDirection routeDirection)
at System.Web.Http.Routing.HttpRoute.ProcessConstraints(HttpRequestMessage request, HttpRouteValueDictionary values, HttpRouteDirection routeDirection)
at System.Web.Http.Routing.HttpRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request)
at System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext)
Ответы
Ответ 1
Предполагая, что вы используете OData V4, вам нужно использовать CollectionParameter
при регистрации функции, и вам не хватает [FromODataUri]
в параметре arrayHere. Кроме того, попробуйте с IEnumerable<int>
вместо массива.
var builder = new ODataConventionModelBuilder();
builder.Namespace = "NS";
builder.EntitySet<TestEntity>("TestEntities");
builder.EntityType<TestEntity>().Collection
.Function("TestFunction")
.ReturnsCollectionFromEntitySet<TestEntity>("TestEntities")
.CollectionParameter<int>("ArrayHere");
С функцией в контроллере, подобной этой...
[HttpGet]
public async Task<IHttpActionResult> TestFunction([FromODataUri] IEnumerable<int> ArrayHere)
{
// Do stuff
}
Теперь вы можете сделать запрос, например...
http://yourRestService/API/TestEntities/NS.TestFunction(ArrayHere=[1,2,3])
В качестве примечания вы также можете принять массив сложных типов. Вам нужно будет закодировать код json для массива и использовать псевдоним параметра, чтобы вы получили что-то вроде этого...
builder.EntityType<TestEntity>().Collection
.Function("TestFunction2")
.ReturnsCollectionFromEntitySet<TestEntity>("TestEntities")
.CollectionParameter<person>("ArrayHere");
и
[HttpGet]
public async Task<IHttpActionResult> TestFunction2([FromODataUri] IEnumerable<person> ArrayHere)
{
// Do stuff
}
http://yourRestService/API/TestEntities/NS.TestFunction2([email protected])[email protected]=%5B%7B%22FirstName%22%3A%22Bob%22%2C+%22LastName%22%3A%22Dole%22%7D%2C%7B%22FirstName%22%3A%22Bill%22%2C+%22LastName%22%3A%22Clinton%22%7D%5D
Ответ 2
Я не знаю, какую версию OData вы используете в настоящее время, но вот решение, которое я реализовал, которое работает для v4 и передает простые массивы методу OData Action
В вашем WebConfig.cs
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<TestEntity>("TestEntities");
builder.Namespace = "NS";
builder.EntityType<TestEntity>()
.Action("TestAction")
.CollectionParameter<string>("ArrayHere");
И ваш метод контроллера здесь
[HttpPost]
public async Task<IHttpActionResult> TestAction([FromODataUri] int key, ODataActionParameters parameters)
{
// Do your thing here..
var invites = parameters["ArrayHere"] as IEnumerable<string>;
}
Полученный запрос конечной точки API должен выглядеть примерно так:
POST http://localhost/odata/TestEntities(1)/NS.TestAction HTTP/1.1
Content-type: application/json
Host: localhost:49255
Content-Length: 37
{"ArrayHere":["hello", "world"]}
Я рассмотрел эту проблему несколько раз и не смог заставить ее работать для функции OData. Я углубился глубже и нашел метод CollectionParameter<>()
, который разобрал все. Я не вернулся и попробовал его с помощью Function
, поскольку я действительно не видел необходимости. Я надеюсь, что это поможет вам