OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
854 stars 475 forks source link

OData 7.4 aspnet core 3.1 Formatter Issues #2139

Open vulegend opened 4 years ago

vulegend commented 4 years ago

Hey everyone. I am having some strange issues using custom formatters for odata 7.4 in aspnetcore 3.1 web api project.

Here are the full details of the issue, hopefully someone will be able to help :

https://stackoverflow.com/questions/61450358/custom-odata-formatter-for-netcore-3-1-webapi-issues

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!

andreyciocan commented 4 years ago

Same issue for me. When I use endpoints.MapODataRoute("odata", "odata", containerBuilder => { containerBuilder.AddService(Microsoft.OData.ServiceLifetime.Singleton, typeof(IEdmModel), sp => GetEdmModel()); }); I get 404. Works with endpoints.MapODataRoute("odata", "odata", GetEdmModel()); only

vulegend commented 4 years ago

Any updates on the issue?

SenyaMur commented 3 years ago

When will be fixed this issue?