Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3.1 nightly with Endpoint Routing: How to MapODataRoute with services? IPerRouteContainer missing #2050

Closed
NetTecture opened this issue Feb 13, 2020 · 12 comments
Assignees

Comments

@NetTecture
Copy link

This is based on the current nightly.
I am stuck with this error.

Code:

            e.MapODataRoute(
                "odata", null,
                b => {
                    b.AddDefaultODataServices(ODataVersion.V401);
                    b.AddService(ServiceLifetime.Singleton, sp => model);
                    b.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new Code.EntityReferenceODataDeserializerProvider(sp));
                    b.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
                        ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", sp));
                }
            );

As you can see, I have to use a little more complex setup because I am putting in my own deserializer for handing some specific reference syntax (odata bind).

I am stuck with an error and no idea how to fix this one. Executing this gets me:

System.InvalidOperationException: 'No service for type 'Microsoft.AspNet.OData.IPerRouteContainer' has been registered.'

Where do I find that one and how do I register it? A look into the builder and the services extensions gave me no sensible methods.

As info: I do have AddOData up in the services configuration. I also played around with EnableDependencyInjection before and after the MapODataRoute call and have no luck. Stuck here - and I need this overload because of the customized deserializer.

@NetTecture NetTecture changed the title 3.1 Endpoint Routing: How to register for deserializer? 3.1 nightly with Endpoint Routing: How to MapODataRoute with services? IPerRouteContainer missing Feb 13, 2020
@xuzhg
Copy link
Member

xuzhg commented Feb 13, 2020

@NetTecture Would you please share your repo?

@xuzhg xuzhg self-assigned this Feb 13, 2020
@NetTecture
Copy link
Author

@xuzhg I do not really have a repo - this is out of the migration of an existing project.

ConfigureServices:

        services.AddControllers();

        services.AddControllersWithViews();
        services.AddRouting();
        services.AddOData(); //.EnableApiVersioning();
        services.TryAddSingleton(ODataSetup.ConfigureEdmModel());

Configure:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors(o => {
            o.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().Build();
        });
        app.UseODataBatching();
        app.UseProblemDetails();
        app.Use(next => ctx => {
            ctx.Request.EnableBuffering(50 * 1024 * 1024); // In memory up to 50mb
            return next(ctx);
        });
        app.UseEndpoints(e => {
            e.
            e.SetTimeZoneInfo(TimeZoneInfo.Utc);
            e.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
            var model = ODataSetup.ConfigureEdmModel();

            //e.MapODataRoute(
            //    "odata", null, model,
            //    new DefaultODataBatchHandler()
            //);
            e.MapODataRoute(
                "odata", null,
                b => {
                    b.AddDefaultODataServices(ODataVersion.V401);
                    b.AddService(ServiceLifetime.Singleton, sp => model);
                    b.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new Code.EntityReferenceODataDeserializerProvider(sp));
                    b.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
                        ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", sp));
                }
            );
            e.EnableDependencyInjection();

        });

I am quite sure I simply have forgotten something, but the error message is not helpfull and given that I work on the level of nightly builds - documentation is kind of not up to date ;)

@NetTecture
Copy link
Author

Just as update - if I replace the MapODataRoute with...

            e.MapODataRoute(
                "odata", null, model,
                new DefaultODataBatchHandler()
            );

then it works, but obviously does not use my deserializer. But instead of the exception, I am getting the $metadata document.

@xuzhg
Copy link
Member

xuzhg commented Feb 13, 2020

@NetTecture I see. Let me take a look and i will update you later.

@NetTecture
Copy link
Author

Thanks. Let me add the info I also get during debugging.

  • The app starts (I get a non odata related api page as start which contains just a version number).

  • The error happens when I call $metadata.

  • The error happns in this line: ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", sp)

  • The following is a copy/paste of the exception details:

System.InvalidOperationException
HResult=0x80131509
Message=No service for type 'Microsoft.AspNet.OData.IPerRouteContainer' has been registered.
Source=Microsoft.Extensions.DependencyInjection.Abstractions
StackTrace:
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNet.OData.Routing.Conventions.AttributeRoutingConvention..ctor(String routeName, IServiceProvider serviceProvider, IODataPathTemplateHandler pathTemplateHandler)
at Microsoft.AspNet.OData.Routing.Conventions.ODataRoutingConventions.CreateDefaultWithAttributeRouting(String routeName, IServiceProvider serviceProvider)

I have only stripped the last element with the line number in the startup file.

@NetTecture
Copy link
Author

@xuzhg Some more info:

There something is really wrong. The following code

            e.MapODataRoute(
                "odata", String.Empty,
                b => {
                    b.AddDefaultODataServices(ODataVersion.V401);
                    b.AddService(
                        ServiceLifetime.Singleton
                        , typeof(Microsoft.AspNet.OData.IPerRouteContainer)
                        , typeof(Microsoft.AspNet.OData.PerRouteContainer)
                    );
                    b.AddService(ServiceLifetime.Singleton, sp => model);
                    b.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new Code.EntityReferenceODataDeserializerProvider(sp));
                    b.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
                        ODataRoutingConventions.CreateDefaultWithAttributeRouting(String.Empty, sp));
                }
            );

results in:

System.InvalidOperationException
HResult=0x80131509
Message=Cannot find the services container for the non-OData route. This can occur when using OData components on the non-OData route and is usually a configuration issue. Call EnableDependencyInjection() to enable OData components on non-OData routes. This may also occur when a request was mistakenly handled by the ASP.NET Core routing layer instead of the OData routing layer, for instance the URL does not include the OData route prefix configured via a call to MapODataServiceRoute().
Source=Microsoft.AspNetCore.OData
StackTrace:
at Microsoft.AspNet.OData.PerRouteContainerBase.GetODataRootContainer(String routeName)
at Microsoft.AspNet.OData.Routing.Conventions.AttributeRoutingConvention..ctor(String routeName, IServiceProvider serviceProvider, IODataPathTemplateHandler pathTemplateHandler)
at Microsoft.AspNet.OData.Routing.Conventions.ODataRoutingConventions.CreateDefaultWithAttributeRouting(String routeName, IServiceProvider serviceProvider)
at Api.Odata.Web.Startup.<>c.b__5_7(IServiceProvider sp) in

This is actually the next issue - no idea where to put EnableDependencyInjection. I Tried before and after MapOdataRoute.

Note that I have UseOData up in ConfigureServices and it is called, but somehow THAT IPerRouteContainer seems to be ignored when the time comes for MapOdataRoute.

@xuzhg
Copy link
Member

xuzhg commented Feb 14, 2020

@NetTecture I see.

Two places are wrong configured in

                "odata", null,
                b => {
                    b.AddDefaultODataServices(ODataVersion.V401);
                    b.AddService(ServiceLifetime.Singleton, sp => model);
                    b.AddService<ODataDeserializerProvider>(ServiceLifetime.Singleton, sp => new Code.EntityReferenceODataDeserializerProvider(sp));
                    b.AddService<IEnumerable<IODataRoutingConvention>>(ServiceLifetime.Singleton, sp =>
                        ODataRoutingConventions.CreateDefaultWithAttributeRouting("IgnoredRouteName", sp));
                }

If you change it as:

app.UseEndpoints(endpoints =>
            {
                endpoints.MapODataRoute(
                    "odata", null,
                    b =>
                    {
                        b.AddService(Microsoft.OData.ServiceLifetime.Singleton, sp => model);
                        b.AddService<ODataDeserializerProvider>(Microsoft.OData.ServiceLifetime.Singleton, sp => new EntityReferenceODataDeserializerProvider(sp));
                        b.AddService<IEnumerable<IODataRoutingConvention>>(Microsoft.OData.ServiceLifetime.Singleton,
                            sp => ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", endpoints.ServiceProvider));
                    });                
            });

It should work.

Noted:

  1. The first parameter string in CreateDefaultWithAttributeRouting method should be the "route name" that you configured.
  2. the second parameter in CreateDefaultWithAttributeRouting method should be the global service provider. That's very bad usage. very bad. It's not my design. I'd like to change it.

Please let me know any other problems.

@gathogojr gathogojr added this to To do in OData WebApi via automation Feb 18, 2020
@xuzhg
Copy link
Member

xuzhg commented Feb 19, 2020

@NetTecture Are you ok for the investigation? Can I close this issue?

@NetTecture
Copy link
Author

@xuzhg Yeah, I actually close it. Some documentation update would definitely not be rejected for other people. THANKS a lot.

OData WebApi automation moved this from To do to Done Feb 20, 2020
@ClementeGao
Copy link

image
Why can't my project use mapodataroute?

@bkwdesign
Copy link

@ClementeGao - did you figure this out? I'm wondering the same thing

@Preethi-Angel
Copy link

@ClementeGao Same issue for me as well. Did you figure this out?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
OData WebApi
  
Done
Development

No branches or pull requests

6 participants