Search code examples
asp.net-web-apiodataodata-v4

Return collection from unbound function gives serialization error


I have the following setup which should return a list of FooBars. But it fails to serialize for some reason :(

Config

ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.Function("Test").ReturnsCollection<FooBar>();
config.MapODataServiceRoute(routeName: "OData",
    routePrefix: "OData",
    model: builder.GetEdmModel());

Unbound Function

[HttpGet]
[ODataRoute("Test")]
public IEnumerable<FooBar> Test()
{
    var foobars = new List<FooBar>();
    // populate list...
    return foobars;
}

Error

Calling localhost/odata/Test gives the below error

{
    "error": {
        "code": "",
        "message": "An error has occurred.",
        "innererror": {
            "message": "The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; odata.metadata=minimal'.",
            "type": "System.InvalidOperationException",
            "stacktrace": "",
            "internalexception": {
                "message": "The related entity set could not be found from the OData path. The related entity set is required to serialize the payload.",
                "type": "System.Runtime.Serialization.SerializationException",
                "stacktrace": "   at System.Web.OData.Formatter.Serialization.ODataFeedSerializer.WriteObject(Object graph, Type type, ODataMessageWriter messageWriter, ODataSerializerContext writeContext)\r\n   at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content, HttpContentHeaders contentHeaders)\r\n   at System.Web.OData.Formatter.ODataMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()"
            }
        }
    }
}

$Metadata snippet

<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" 
    xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
    <edmx:DataServices>
        <Schema Namespace="MyNamespace" 
            xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <EntityType Name="FooBar">
                <Key>
                    <PropertyRef Name="Id" />
                </Key>
                <Property Name="Id" Type="Edm.Int32" Nullable="false" />
                <Property Name="A" Type="Edm.String" />
                <Property Name="B" Type="Edm.Int32" Nullable="false" />
                <Property Name="C" Type="Edm.Int32" Nullable="false" />
            </EntityType>
            <EntityType Name="FooBarD" BaseType="MyNamespace.FooBar">
                <Property Name="D" Type="Edm.Int32" Nullable="false" />
            </EntityType>
            <EntityType Name="FooBarE" BaseType="MyNamespace.FooBar">
                <Property Name="E" Type="Edm.Int32" Nullable="false" />
            </EntityType>
        </Schema>
        <Schema Namespace="Default" 
            xmlns="http://docs.oasis-open.org/odata/ns/edm">
            <Function Name="Test">
                <ReturnType Type="Collection(MyNamespace.FooBar)" />
            </Function>
            <EntityContainer Name="Container">
                <FunctionImport Name="Test" Function="Default.Test" IncludeInServiceDocument="true" />
            </EntityContainer>
        </Schema>
    </edmx:DataServices>
</edmx:Edmx>

Solution

  • Turns out that because I had this builder.EntitySet<FooBar>("BAM"); set, I needed to call ReturnsCollectionFromEntitySet instead.

    ODataModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<FooBar>("BAM"); // previously omitted from example
    builder.Function("Test").ReturnsCollectionFromEntitySet<FooBar>("BAM")();
    config.MapODataServiceRoute(routeName: "OData",
        routePrefix: "OData",
        model: builder.GetEdmModel());