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

Using Startup class not from the execution Assembly #7315

Open
YogiBear52 opened this issue Feb 6, 2019 · 16 comments
Open

Using Startup class not from the execution Assembly #7315

YogiBear52 opened this issue Feb 6, 2019 · 16 comments
Labels
affected-medium This issue impacts approximately half of our customers area-hosting Includes Hosting area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions enhancement This issue represents an ask for new feature or an enhancement to an existing one severity-minor This label is used by an internal tool
Milestone

Comments

@YogiBear52
Copy link

Describe the bug

Hey,

I am trying to use a Startup class like that
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().Build().Run()
When the Startup class is located in the execution assembly everything works fine.
When the Startup class is located in a different assembly, the service runs but things go wrong during requests - the service throws a 404 without any explanation.

Why is it important for me to put my Startup class in a different assembly?
Because I have many services that are using the exact same Startup configuration and I want to prevent duplication.

To Reproduce

Steps to reproduce the behavior:

  1. Creating new template of Asp.net core 2.2 WebApi
  2. Request is working
  3. Creating new Project of .Net Standard 2.0
  4. Moved the Startup class from the Asp.net Core project to the .Net Standard Project
  5. Added these two nugets to the .Net Standard project : 'Microsoft.AspNetCore.Mvc' and 'Microsoft.AspNetCore.Diagnostics' Nugets.
  6. Add Reference from the Asp.net core project the .Net Standard project
  7. Request is not working

Thank you :)

@analogrelay
Copy link
Contributor

Can you share a runnable sample that reproduces the problem? That way we get an exact repro and can investigate in depth.

@davidfowl
Copy link
Member

It might be worth looking at this issue again but I don't know if we'll have time to fix it. The workaround is to do something like this:

public class Program
{
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            // Ignore the startup class assembly as the "entry point" and instead point it to this app
            .UseSetting(WebHostDefaults.ApplicationKey, typeof(Program).GetTypeInfo().Assembly.FullName); 
}

We've had this come up a number of times and it might make sense to handle this somehow.

https://github.com/aspnet/Hosting/issues?q=is%3Aissue+ApplicationKey+is%3Aclosed

@davidfowl
Copy link
Member

We could clean this up a bit by adding a UseEntryPoint method that sets the entry point assembly. Another thing we should consider doing is splitting application name and entrypoint (but this would be a breaking change for people that originally worked around this issue via the above). The application entry point is used by MVC today to figure out where controllers are so it doubles as both the "application name" and the "entrypoint" assembly.

@analogrelay
Copy link
Contributor

Seems reasonable. Any reason we can't default the entry point to the assembly containing the Startup type? Also the name UseEntryPoint is a bit overloaded (especially with some of the generic host stuff I've talked about with @glennc). Perhaps UsePrimaryAssembly or UseApplicationAssembly?

Also, does MVC have options to add/change the assembly that's scanned?

@davidfowl
Copy link
Member

Any reason we can't default the entry point to the assembly containing the Startup type?

We do and that's the bug. The Startup class is in the class library and controllers are in the main application.

Also the name UseEntryPoint is a bit overloaded (especially with some of the generic host stuff I've talked about with @glennc). Perhaps UsePrimaryAssembly or UseApplicationAssembly?

Yea, lets bikeshed the name. @khellang Proposed a PR a while back with similar functionality. We could even decide that this doesn't belong in hosting.

Also, does MVC have options to add/change the assembly that's scanned?

Not the primary assembly. This happens very early (during DI setup) https://github.com/aspnet/AspNetCore/blob/258d34e3828a1870a16d13cd3c62d1b7a65acc4a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs#L81-L88

@analogrelay
Copy link
Contributor

We do and that's the bug.

Gotcha, just re-read the issue.

@khellang
Copy link
Member

What makes it super confusing is that it's called ApplicationName with no reference to assemblies.

I remember a couple of issues where people got tripped up because things stopped working by just changing the application name. Giving an instance of your app a pet name is a perfectly valid scenario, and so is changing the "entry point assembly" (not to be confused with an IL entrypoint). I think both should be allowed.

It's also kinda ironic that we're on the third breaking iteration of the IHostingEnvironment/IWebHostEnvironment and haven't used the opportunity to correct the naming mistake 😅

@khellang
Copy link
Member

khellang commented Apr 12, 2019

And, for reference: aspnet/Hosting#1180. It's 3.0 season - time to break someone! 😉

@davidfowl
Copy link
Member

It's also kinda ironic that we're on the third breaking iteration of the IHostingEnvironment/IWebHostEnvironment and haven't used the opportunity to correct the naming mistake 😅

We're not though. We haven't broken anything until now. We added new interfaces and it's time to add the new property while we still can (during 3.0).

@davidalpert
Copy link

davidalpert commented Jan 30, 2020

I'm confused why #14183 was closed as "no work to do" - can anyone shed light on that decision? This is the closest issue I can find.

I spent some time digging through the code in this repo trying to figure out why my ASPNETCORE 3.x app was not respecting the APPLICATION_NAME set up in my app configuration and learned that using the DefaultWebHostBuilder and configuring a Startup class injects an additional configuration provider (last so that it overrides my app configuration) and forces the app name to the AssemblyName of the DLL containing the Startup class.

I'm curious why it does not simply respect the APPLICATION_NAME as configured by IConfigurationBuilder instance returned from the ConfigureAppConfiguration method?

I'm using a Generic Host and then calling ConfigureWebHostDefaults, so maybe this is a holdover behavior in the Web Host from before the Generic Host was created?

It appears to place the Generic Host guidance for setting ApplicationName in conflict with the Web Host guidance for setting ApplicationName

Eventually I'm hoping that the responsibility of overriding the ApplicationName config setting can be removed from the web host responsibility and handled in a consistent way by the Generic Host

This line seems to be the unexpected (to me) behavior after reading this guidance on configuring the ApplicationName in the Generic Host docs - maybe I can reproduce that in a unit test

@hellfirehd
Copy link

What makes this worse is that the Identity Templates are using @Environment.ApplicationName. So the Title and Copyright are now the name of my assembly, not the actual Application Name that I want the public to see.

It's really a shame that this wasn't addressed in 3.0. Maybe 5.0?

@alberto-chiesa
Copy link

Question (and proposal): why are we not using directly Assembly.GetEntryAssembly() as a first choice?

In my case (I don't use a startup class), I just put a line after calling Configure with:

        protected override IHostBuilder CreateHostBuilder()
            => new HostBuilder()
                .UseServiceProviderFactory(ctx => ServiceProviderFactory)
                .ConfigureWebHost(webHostBuilder => webHostBuilder
                    .UseKestrel(options => { })
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .ConfigureServices(ConfigureServices)
                    .Configure(ConfigureWebHost)
                    .UseSetting(WebHostDefaults.ApplicationKey, Assembly.GetEntryAssembly().FullName));

And everything works again.
Seems to me it's a pretty reasonable default.
Maybe it's not when a Framework launcher is used? I'm on Windows, and using self contained exe, so the whole "deploy a dll" is something I never played with.
Maybe a fallback logic could be in place.

@Tratcher Tratcher added affected-few This issue impacts only small number of customers severity-minor This label is used by an internal tool affected-medium This issue impacts approximately half of our customers enhancement This issue represents an ask for new feature or an enhancement to an existing one labels Nov 6, 2020 — with ASP.NET Core Issue Ranking
@Tratcher Tratcher removed the affected-few This issue impacts only small number of customers label Nov 6, 2020
@hillin
Copy link

hillin commented Jun 23, 2021

Question (and proposal): why are we not using directly Assembly.GetEntryAssembly() as a first choice?

In my case (I don't use a startup class), I just put a line after calling Configure with:

        protected override IHostBuilder CreateHostBuilder()
            => new HostBuilder()
                .UseServiceProviderFactory(ctx => ServiceProviderFactory)
                .ConfigureWebHost(webHostBuilder => webHostBuilder
                    .UseKestrel(options => { })
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .ConfigureServices(ConfigureServices)
                    .Configure(ConfigureWebHost)
                    .UseSetting(WebHostDefaults.ApplicationKey, Assembly.GetEntryAssembly().FullName));

And everything works again.
Seems to me it's a pretty reasonable default.
Maybe it's not when a Framework launcher is used? I'm on Windows, and using self contained exe, so the whole "deploy a dll" is something I never played with.
Maybe a fallback logic could be in place.

Using Assembly.GetEntryAssembly().FullName does not work for us, but Assembly.GetEntryAssembly().GetName().Name does work. Thanks!

@davidfowl
Copy link
Member

Entry assembly would break things like tests where the test process is the entry assembly and your code is hosted as a library inside of the test process.

@davidalpert
Copy link

Entry assembly would break things like tests where the test process is the entry assembly and your code is hosted as a library inside of the test process.

What kind of a test might be broken by this scenario?

@akonyer
Copy link

akonyer commented Feb 1, 2023

I am running into this issue using the pact contract testing framework.

I am spinning up a separate web server for API contract testing, and I'm spinning it up from a unit testing assembly.

I spent a couple of days trying to figure out why I was only getting 404's when I wrap my test startup class in another one to add some middleware for testing.

@davidfowl's solution above helped me resolve this issue. Thanks a lot :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affected-medium This issue impacts approximately half of our customers area-hosting Includes Hosting area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions enhancement This issue represents an ask for new feature or an enhancement to an existing one severity-minor This label is used by an internal tool
Projects
None yet
Development

No branches or pull requests