dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.22k stars 9.95k forks source link

Adding a constructor to a controller breaks hot reloading (dotnet watch) #49226

Open wavedeck opened 1 year ago

wavedeck commented 1 year ago

Is there an existing issue for this?

Describe the bug

When I add a constructor for dependency injection to my controller (while hot reloading is enabled) i receive a runtime error:

InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'MyApplication.UserController'. There should only be one applicable constructor.

This issue is resolved by restarting the build, but I should not have to rebuild and restart my application every time a constructor is added to a controller. (Especially without a hint to do so)

Interestingly enough, hot reloading fails upon then removing the private property and constructor again with a warning that this change requires a restart of the application. This does not happen in the first step of adding the constructor.

Code for reference:

UserController.cs

[Route("users")]
[ApiController]
public class UserController : Controller
{
    private readonly ILogger<UserController> _logger; // adding this

    public UserController(ILogger<UserController> logger) // and this
    {
        _logger = logger;
    }

    [HttpGet("")]
    public IActionResult GetAll()
    {
        _logger.LogInformation("Hello from Logger"); // will result in error using hot reloading
        return Ok("Hello from /users");
    }

    [HttpGet("info")]
    public IActionResult GetInfo()
    {
        return Ok("Hello from /users/info");
    }

    [HttpGet("{id:int}")]
    public IActionResult GetOne(int id)
    {
        return Ok("Hello from /users/" + id);
    }
}

Expected Behavior

Hot Reloading using (dotnet watch) should not result in InvalidOperationException: Multiple constructors accepting all given argument types have been found while adding a constructor to a previously constructor-less controller.

Steps To Reproduce

  1. Create a ASP.NET webapi (minimal api) project.
  2. Configure the controller service on the builder (builder.Services.AddControllers();) and enable the controller route mapping on the application instance (app.MapControllers();).
  3. Add a simple controller without a constructor.
  4. Start the application with hot reload enabled ($ dotnet watch).
  5. Add a constructor (meant for DI) to the controller.
  6. Save the file that of the affected controller (hot reload should take place).
  7. Notice that the added constructor breaks the controller upon making a HTTP request.

Exceptions (if any)

InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'FinanceManager.UserController'. There should only be one applicable constructor.

.NET Version

7.0.305

Anything else?

My Environment

dotnet --info

.NET SDK: Version: 7.0.305 Commit: 98e1b6c381

Runtime Environment: OS Name: Mac OS X OS Version: 13.3 OS Platform: Darwin RID: osx.13-arm64 Base Path: /usr/local/share/dotnet/sdk/7.0.305/

Host: Version: 7.0.8 Architecture: arm64 Commit: 4b0550942d

.NET SDKs installed: 6.0.303 [/usr/local/share/dotnet/sdk] 7.0.101 [/usr/local/share/dotnet/sdk] 7.0.305 [/usr/local/share/dotnet/sdk]

.NET runtimes installed: Microsoft.AspNetCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.1 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 7.0.8 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App] Microsoft.NETCore.App 6.0.8 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.1 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App] Microsoft.NETCore.App 7.0.8 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found: None

Environment variables: Not set

global.json file: Not found

mitchdenny commented 1 year ago

Interesting find. So what appears to be going on here is that there is a default public constructor present on your controller. When you update the source code the new constructor is added to the type but the old constructor isn't. To be clear it doesn't appear to be the dotnet watch command itself that is throwing an error here, it will happily update the type to add the new constructor - it just isn't realizing that the existing (default) shouldn't exist anymore.

ghost commented 11 months ago

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues. To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

mkArtakMSFT commented 11 months ago

This is currently blocked on the following issues and should be automatically resolved when these are fixed: