4
votes

So i am building a service that has to be consumed through OData and i am having a really difficult time figuring how to add custom formatters to it. I need my OData serializer to ignore null values when serializing data. I have created these 2 to achieve that :

public class SmartODataSerializerProvider : DefaultODataSerializerProvider
{
    private readonly SmartODataEntityTypeSerializer _entityTypeSerializer;

    public SmartODataSerializerProvider(IServiceProvider rootContainer)
        : base(rootContainer)
    {
        _entityTypeSerializer = new SmartODataEntityTypeSerializer(this);
    }

    public override ODataEdmTypeSerializer GetEdmTypeSerializer(Microsoft.OData.Edm.IEdmTypeReference edmType)
    {
        // Support for Entity types AND Complex types
        if (edmType.Definition.TypeKind == EdmTypeKind.Entity || edmType.Definition.TypeKind == EdmTypeKind.Complex)
            return _entityTypeSerializer;
        else
            return base.GetEdmTypeSerializer(edmType);
    }
}

And

public class SmartODataEntityTypeSerializer : ODataResourceSerializer
{
    public SmartODataEntityTypeSerializer(ODataSerializerProvider provider)
        : base(provider) { }

    /// <summary>
    /// Only return properties that are not null
    /// </summary>
    /// <param name="structuralProperty">The EDM structural property being written.</param>
    /// <param name="resourceContext">The context for the entity instance being written.</param>
    /// <returns>The property be written by the serilizer, a null response will effectively skip this property.</returns>
    public override Microsoft.OData.ODataProperty CreateStructuralProperty(Microsoft.OData.Edm.IEdmStructuralProperty structuralProperty, ResourceContext resourceContext)
    {
        var property = base.CreateStructuralProperty(structuralProperty, resourceContext);
        return property.Value != null ? property : null;
    }
}

These were provided on another stack overflow question. However, the issue arises when i try to use this serializer. I already have an odata endpoint that's working (it just serializes everything with null) and when i apply the following configuration to it i keep getting '404 Not Found' on the same EP that works without it.

app.UseEndpoints(endpoints =>
        {
            endpoints.MapODataRoute("odata", "odata", a =>
            {
                a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel(app.ApplicationServices));
                a.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(ODataSerializerProvider), sp => new SmartODataSerializerProvider(sp));
            });
            endpoints.EnableDependencyInjection();
            //endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));
            endpoints.MapControllers();
        });

This is the endpoints settings. I commented out the line that makes it work but without custom formatters. Here's the IEdmModel function used in the setup :

 private static IEdmModel GetEdmModel(IServiceProvider services)
    {
        ODataConventionModelBuilder builder = new ODataConventionModelBuilder(services);

        builder.Namespace = "RS";
        builder.EntitySet<PropertyIndexODataModel>("Properties");
        builder.EntitySet<ReportResultModel>("Reports");

        var function = builder.EntityType<ReportResultModel>().Collection.Function("Generate");

        function.Parameter<int>("listId");
        function.CollectionParameter<string>("functionsToRun");
        function.ReturnsCollectionFromEntitySet<ReportResultModel>("Reports");

        return builder.GetEdmModel();
    }

So when i apply this odataroute i keep getting the 404. When i remove it and go back to 'endpoints.MapODataRoute("odata", "odata", GetEdmModel(app.ApplicationServices));' it works without problems.

This seems like a very trivial thing but i searched everywhere and i still couldn't get it to work. I am using OData 7.4 and netcore 3.1. Thanks in advance!

1
Same situation. Have you find any solutions ? - Max
@Max Nop. I've even posted on the official repository and it has been "under investigation" for a few months now - Vulegend

1 Answers

4
votes

I think what's happening here is that the MapODataRoute is missing the routing configuration. Try adding the following after the SmartODataSerializerProvider registration:

 a.AddService<IEnumerable<IODataRoutingConvention>>(Microsoft.OData.ServiceLifetime.Singleton, sp =>
    ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", endPoints.ServiceProvider));  

I had the same problem and this fixed it for me. See this issue for more details.