Unable to use routing in when creating host and using controllers from separate projects. When using everything from one project, routes work fine.
Steps to reproduce the behavior:
Routes, views, razor, etc. should work regardless of which project the host builder, views and controllers are in (as they do in .NET core 2.2).
Can you provide a running project?
Working on it
For some reason, .net core 2 tests can find controllers in other assemblies. This seems to have broken in .net core 3. For now my hacky work-around is this (new code added for assembly part that was not needed in .net core 2):
IMvcBuilder mvcBuilder = services.AddMvc((options) =>
{
options.EnableEndpointRouting = true;
});
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
try
{
if (assembly.GetTypes().Any(t => t.IsSubclassOf(typeof(Controller))))
{
mvcBuilder.AddApplicationPart(assembly);
}
}
catch
{
// bugs in unit test framework throw exceptions for weird assemblies like intellitrace
}
}
mvcBuilder.SetCompatibilityVersion(CompatibilityVersion.Latest).AddJsonOptions(options =>
{
options.JsonSerializerOptions.IgnoreNullValues = true;
});
With this solution unfortunately now my razor views cannot find any dlls, including system etc, even with preserve compilation context set in the csproj...
Still waiting for a repro.
Still trying to strip my very large project down into the smallest reproducible case, Will post back if I can get it.
I can reproduce it on my machine here regularly but not able as of yet to upload to a repo. Is it possible to do a screen share?
Did you try to reproduce it in a new project?
Yes, so far it can find the controller in a different assembly but I have not tried razor yet, just raw json. Copying the code over to my project and suddenly I get 404 errors with the same controller. Very bizzare.
public static async Task Main(string[] args)
{
using (var host = CreateHostBuilder(args).Build())
{
await host.RunAsync();
}
}
public static IHostBuilder CreateHostBuilder(params string[] args)
{
return Host.CreateDefaultBuilder(args).ConfigureLogging((logBuilder) =>
{
logBuilder.AddConsole().SetMinimumLevel(LogLevel.Trace);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.Configure((ctx, app) =>
{
if (ctx.HostingEnvironment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthorization();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapControllerRoute("default", "/{action}/{id?}", new { action = "Index", controller = "Home" });
});
})
.ConfigureServices((serviceCollection) =>
{
serviceCollection.AddAuthorization();
serviceCollection.AddRazorPages();
})
.ConfigureAppConfiguration((ctx, configBuilder) =>
{
configBuilder.Build();
});
});
}
In my project, I hit same controller Index method, get this errors:
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/1.1 GET http://localhost:5000/
dbug: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
Wildcard detected, all requests with hosts will be allowed.
trce: Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[2]
All hosts are allowed.
dbug: Microsoft.AspNetCore.Mvc.Razor.Compilation.DefaultViewCompiler[4]
Initializing Razor view compiler with no compiled views.
dbug: Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1000]
No candidates found for the request path '/'
dbug: Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[2]
Request did not match any endpoints
dbug: Microsoft.AspNetCore.Server.Kestrel[9]
Connection id "0HLQFJ9TFKQ6A" completed keep alive response.
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 27.218ms 404
I have a repro project. It is attached. Nice and small.
Download --> NetCore3RouteTestFail.zip
Instructions:
@davidfowl
Try add __UseSetting__ on _CreateHostBuilder_ function.

On _Program.Main_ we pass __AssemblyName__.

And to make easy lunch the app, you can modify __launchSettings.json__ (in _Properties_ folder on project _NetCore3RouteTestFail_).

@jjxtra Thanks for contacting us and thanks for the repro.
I've looked at the project you sent us and I believe the reason you are not finding the controllers when using HostCreator is because it doesn't have a reference to the Controllers project.
The way discovery works in MVC is thought metadata stamped into the assembly at build time. When you try to start the host it looks for an assembly with the application name to find the assembly level attributes that tell it which assemblies to use for discovering controllers and other MVC primitives.
@soeleman answer will probably help here, as setting the application name should fix this issue. That said, if you are doing this for testing purposes I would highly recommend using https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.0 instead.
Let us know if that fixes your issue.
Maybe this is related or not but if one tries to put a Page into an RCL the page cannot be routed too with the default startup.cs. Not sure if that is supported or not.
@chassq I don't think your issue is related.
If you think you've found an issue open a separate issue describing your scenario so that we can track it.
So has this changed from net core 2.2? I had the same pattern in 2.2 and it worked fine, no extra stuff needed.
@jjxtra This might have been a change in hosting as a result of the move to generic host.
@Tratcher Did we change something in how we set the app name from 2.2 to 3.0 with regards to UseStartup?
Not intentionally, but there may be a subtle ordering difference or similar.
I don't know if it helps, but the legacy web host builder also has an identical problem in net core 3. It's possible that it and the new host builder share the same common code now.
Thanks for contacting us. We believe that the question you've raised have been answered. If you still feel a need to continue the discussion, feel free to reopen it and add your comments.
I will attempt the suggested fix this evening and post back
This worked for the regular project, unfortunately the unit test project still fails. I am unable to re-open this issue.
I don't see any good documentation on this usesetting with this key, it feels a little bit hacky. Trying it in my other big project also did not resolve the 404 error so I am still concerned that something is not working quite right in .net core 3.
@mkArtakMSFT
I see this link: https://github.com/dotnet/core/blob/master/release-notes/3.0/preview/3.0.0-preview-known-issues.md
I believe the issue "Referencing 3.0.0 MVC libraries don't work as intended: There are several issues with referencing a 3.0.0 MVC library" is not completely resolved, just from what I am seeing...
I can add application part for all assembly with typeof(Controller) and that allows api controllers to be found, but anything with razor still gives this, even with these project settings:
IMvcBuilder mvcBuilder = services.AddRazorPages().AddRazorRuntimeCompilation(options =>
{
}).AddMvcOptions((options) => options.EnableEndpointRouting = true);
foreach (Assembly a in IPBanExtensionMethods.GetAssembliesWithType(typeof(Controller)))
{
mvcBuilder.AddApplicationPart(a);
}
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<CopyRefAssembliesToPublishDirectory>true</CopyRefAssembliesToPublishDirectory>
<PreserveCompilationReferences>true</PreserveCompilationReferences>
<PreserveCompilationContext>true</PreserveCompilationContext>

I鈥檒l take another look
@jjxtra With the repro you provided I did the following:
csharp
var host = HostCreator.HostBuilder.CreateHostBuilder().Build(); // broken
//var host = NetCore3RouteTestFail.Program.CreateHostBuilder().Build(); // works
CreateHostBuilder(args).Build().Start(); (This needs to be added to program main).
The other issue that you are seeing is likely caused by using a different app name, as it results in loading the wrong dependencycontext which is what razor uses to perform recompilation.
My original recommendation still stands, if you are doing this for testing purposes use WebApplicationFactory as it handles all these concerns.
I'm closing this issue as there's no more action to be taken here.
I was able to hack around the issue by doing the following. Posting it here for anyone else who comes across this issue in .NET core 3. I really hope it saves people the headache I went through.
You have to capture the web host environment before the service provider is built. You can do this in ConfigureAppConfiguration, .i.e.:
builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) =>
{
this.WebHostEnvironment = hostingContext.HostingEnvironment;
// rest of your configuration code if any
});
Then, in ConfigureServices, set the application name, i.e.
webBuilder.ConfigureServices((ctx, serviceCollection) =>
{
this.WebHostEnvironment.ApplicationName = Assembly.GetEntryAssembly().GetName().Name;
// rest of your services configuration code if any
});
Unit tests unfortunately are still broken. Something does not wire up right when running under testhost and razor views cannot find any assemblies, despite using the appropriate csproj properties like preserve compilation context. I am still looking into a hack/workaround for that.
@jjxtra , I'm trying to understand your resolution and trying to apply it to #15046 to be able to find a fix, but I still don't see how to relate them. Could you please give a bit more insights on how you was able to solve the issue?
I can confirm the .UseSetting(WebHostDefaults.ApplicationKey, typeof(Startup).Assembly.GetName().Name) works like a charm.
Sample code:
WebHost.CreateDefaultBuilder()
.UseStartup<TestStartup>()
.UseSetting(WebHostDefaults.ApplicationKey, typeof(Startup).Assembly.GetName().Name)
.Build()
The only issue remaining still is that razor does not work in unit tests, something to do with testhost I think, but not sure...