JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
563 stars 118 forks source link

Problem with using .Net 6 minimal hosting #323

Closed tmc101 closed 2 years ago

tmc101 commented 2 years ago

Hi

I'm trying to use Lamar with the default .Net 6 Web API project generated by Visual Studio 2022.

The following minimal Program.cs works (without Lamar):

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

var app = builder.Build();

app.MapControllers();

app.Run();

I can access their example WeatherForecastController here:

http://localhost:5156/weatherforecast

If I then add this Lamar dependency:

<PackageReference Include="Lamar.Microsoft.DependencyInjection" Version="7.1.1" />

According the the documentation here:

  https://jasperfx.github.io/lamar/guide/#lamar-with-asp-net-core-minimal-hosting

I would expect this Program.cs to work:

using Lamar.Microsoft.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseLamar((context, registry) =>
{
    registry.AddControllers();
});

var app = builder.Build();

app.MapControllers();

app.Run();

But I get a 404 error when trying to access http://localhost:5156/weatherforecast.

The Kestrel log shows:

      info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
            Request starting HTTP/1.1 GET http://localhost:5156/weatherforecast - -
      dbug: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
            Wildcard detected, all requests with hosts will be allowed.
      dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1000]
            No candidates found for the request path '/weatherforecast'
      dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[2]
            Request did not match any endpoints
      dbug: Microsoft.AspNetCore.Server.Kestrel.Connections[9]
            Connection id "0HMF4N473N8HB" completed keep alive response.
      info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
            Request finished HTTP/1.1 GET http://localhost:5156/weatherforecast - - - 404 0 - 541.4319ms

However this Program.cs works fine:

using Lamar.Microsoft.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseLamar();

builder.Host.ConfigureContainer<Lamar.ServiceRegistry>(services =>
{
    services.AddControllers();
});

var app = builder.Build();

app.MapControllers();

app.Run();

Is the documentation at https://jasperfx.github.io/lamar/guide/#lamar-with-asp-net-core-minimal-hosting still applicable to .Net 6 minimal hosting?

tmc101 commented 2 years ago

This issue can be seen in lamar\src\LamarWithMinimalApiOnNet6\Program.cs since commit:

https://github.com/JasperFx/lamar/commit/5c6cff1a118b395437e6e518b1241b810c0d73b4

Accessing the HelloController fails:

https://localhost:44383/api/Hello

tmc101 commented 2 years ago

This look like a duplication of https://github.com/JasperFx/lamar/issues/320

The documentation needs to be updated - as the 'Hello World' example doesn't work.

jeremydmiller commented 2 years ago

@tmc101 It did at the time the docs were produced. I happily take pull requests for that if you're interested.

tmc101 commented 2 years ago

Hi @jeremydmiller Thanks for the reply.

I guess a change in dotnet 6 could have broken commit 5c6cff1a118.

In https://github.com/JasperFx/lamar/issues/320 you recommend putting the AddControllers() call outside of UseLamar().

This does work:

// use Lamar as DI.
builder.Host.UseLamar((context, registry) =>
{
    // register services using Lamar
    registry.For<ITest>().Use<MyTest>();
    registry.IncludeRegistry<MyRegistry>();

});

// add the controllers
builder.Services.AddControllers();

Would this be the canonical way to use Lamar with .Net 6 minimal hosting?

If so I can submit a pull request to update lamar\src\LamarWithMinimalApiOnNet6\Program.cs and the docs.

jeremydmiller commented 2 years ago

@tmc101 Short term, yeah, take AddControllers() outside of UseLamar(). I'll try to address this early next week and change the mechanics ever so slightly of UseLamar() so that it's being applied against the host builder's ServiceCollection and see if I can fix that permanently.

I'd rather hold off just at the moment to see if that can be fixed. Folks don't read the docs generally anyway, so I'd rather try to actually fix it.

jeremydmiller commented 2 years ago

I'm finally looking at this today.

jeremydmiller commented 2 years ago

*I have the "fix" for this in a local branch, but need a touch of assistance from someone else to get a test in place for the behavior. Once that's done, I'll push a version of Lamar with this fix.

Temporary Workaround

Just move the call to AddControllers() outside of the UseLamar() block like so:

var builder = WebApplication.CreateBuilder(args);

// DO IT HERE!
builder.Host.ConfigureServices(s => s.AddControllers());

// use Lamar as DI.
builder.Host.UseLamar((context, registry) =>
{
    // register services using Lamar
    registry.For<ITest>().Use<MyTest>();
    registry.IncludeRegistry<MyRegistry>();

    // NOT HERE!
   // registry.AddControllers();
});

var app = builder.Build();
app.MapControllers();

app.MapGet("/", (ITest service) => service.SayHello());

app.Run();