如何在ASP.NET Web API实现中将Array传递给OData函数

本文关键字:Array 函数 OData 实现 ASP NET API Web | 更新日期: 2023-09-27 18:28:29

OData V4的规范规定它必须是可能的:https://issues.oasis-open.org/browse/ODATA-636.

"复杂类型和数组只能通过参数别名"

当我试图传递具有OData参数别名的数组时,会发生异常。

/TestEntities/NS.TestFunction(ArrayHere=@p)@p=[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)

如何在ASP.NET Web API实现中将Array传递给OData函数

假设您使用的是OData V4,则在注册函数时需要使用CollectionParameter,并且在arrayHere参数中缺少[FromODataUri]。另外,请尝试使用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进行url编码,并使用参数别名,这样您最终会得到这样的结果。。。

 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(ArrayHere=@ArrayData)?@ArrayData=%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

我不知道您当前使用的是哪种版本的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,因为我并不觉得有必要。我希望这能帮助你摆脱