Startup.cs in a self-hosted .NET Core Console Application
All .NET Core
applications are composed of well-crafted independent libraries and packages which you're free to reference and use in any type of application. It just so happens that an Asp.net core
application comes preconfigured to reference a lot of those libraries and exposes an http endpoint.
But if it's Dependency Injection you need for your console app, simply reference the appropriate library. Here's a guide: https://andrewlock.net/using-dependency-injection-in-a-net-core-console-application/
How to run .NET Core Console app using generic host builder
EDIT: An update for .NET 6 is below ↓
Not much has changed with .NET 7.
I'd start off with the default worker
template. It comes with necessary packages pre-installed. If you already have a project, install Microsoft.Extensions.Hosting
package.
dotnet new worker -n MyCli
Then open up the Program.cs
and build the host. Remove the Worker
hosted service if you don't want to go with the hosted service route.
public class Program
{
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
// remove the hosted service
// services.AddHostedService<Worker>();
// register your services here.
});
}
Build your logic:
internal class MyService
{
// you can also inject other services
private ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.LogInformation("Doing something");
}
}
Then register the class inside .ConfigureServices method
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddTransient<MyService>();
});
Now you can resolve and call it inside the Main
method:
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
var myService = host.Services.GetRequiredService<MyService>();
myService.DoSomething();
}
.NET 6 update
With .NET 6, boilerplate is reduced significantly. We can rewrite our Program.cs
as:
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services => { services.AddTransient<MyService>(); })
.Build();
var my = host.Services.GetRequiredService<MyService>();
await my.ExecuteAsync();
class MyService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public async Task ExecuteAsync(CancellationToken stoppingToken = default)
{
_logger.LogInformation("Doing something");
}
}
How to setup the DI container in a .NET Core console app?
The call to BuildServiceProvider
should be made after all services are registered.
There's no need to write all of this code though. Since you use so many extensions already, it's better (and easier) to use the generic Host, the same way an ASP.NET Core application does and use its ConfigureServices
, ConfigureAppConfiguration
methods:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(configuration =>
{
configuration....;
});
.ConfigureServices((hostContext, services) =>
{
var myConfigurationSection = configuration.GetSection("app");
services.AddSingleton<IValidateOptions<AppOptions>, AppOptionsValidator>();
services.Configure<AppOptions>(myConfigurationSection);
});
}
Configuration is available through the HostBuilderContext.Configuration property.
CreateDefaultBuilder sets the current folder, configures environment variables and the use of appsettings.json
files so there's no need to add them explicitly.
Appsettings.json copy settings
In a web app template, appsettings.json
files are added automatically with the Build Action
property set to Content
and the Copy to Output
action to Copy if Newer
.
There are no such files in a Console app. When a new appsettings.json
file is added by hand, its Build Action
is None
and Copy
to Never
. When the application is debugged the current directory is bin\Debug
. With the default settings, appsettings.json
won't be copied to bin/Debug
Build Action
will have to change to Content
and Copy
should be set to Copy if Newer
or Copy Always
.
Using Startup class in ASP.NET5 Console Application
What you're looking for is the right idea, but I think you'll need to back up a moment.
Firstly, you may have noticed that your default Program
class isn't using static methods anymore; this is because the constructor actually gets some dependency injection love all on its own!
public class Program
{
public Program(IApplicationEnvironment env)
{
}
public void Main(string[] args)
{
}
}
Unfortunately, there aren't as many of the services you're used to from an ASP.NET 5 hosting environment registered; thanks to this article and the IServiceManifest
you can see that there's only a few services available:
Microsoft.Framework.Runtime.IAssemblyLoaderContainer
Microsoft.Framework.Runtime.IAssemblyLoadContextAccessor
Microsoft.Framework.Runtime.IApplicationEnvironment
Microsoft.Framework.Runtime.IFileMonitor
Microsoft.Framework.Runtime.IFileWatcher
Microsoft.Framework.Runtime.ILibraryManager
Microsoft.Framework.Runtime.ICompilerOptionsProvider
Microsoft.Framework.Runtime.IApplicationShutdown
This means you'll get the joy of creating your own service provider, too, since we can't get the one provided by the framework.
private readonly IServiceProvider serviceProvider;
public Program(IApplicationEnvironment env, IServiceManifest serviceManifest)
{
var services = new ServiceCollection();
ConfigureServices(services);
serviceProvider = services.BuildServiceProvider();
}
private void ConfigureServices(IServiceCollection services)
{
}
This takes away a lot of the magic that you see in the standard ASP.NET 5 projects, and now you have the service provider you wanted available to you in your Main
.
There's a few more "gotchas" in here, so I might as well list them out:
- If you ask for an
IHostingEnvironment
, it'll be null. That's because a hosting environment comes from, well, ASP.Net 5 hosting. - Since you don't have one of those, you'll be left without your
IHostingEnvironment.EnvironmentName
- you'll need to collect it from the environment variables yourself. Which, since you're already loading it into your Configuration object, shouldn't be a problem. (It's name is "ASPNET_ENV", which you can add in the Debug tab of your project settings; this is not set for you by default for console applications. You'll probably want to rename that, anyway, since you're not really talking about an ASPNET environment anymore.)
Start a C# web API inside an application
ms recomends this syntax for net 6 minimal apis https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0
app.MapGet("/MyEndpoint", () =>
{
return "it works";
});
as an example, I am using this in my apis , when I am testing them from Visual Studio since I don't like an empty screen.
At first I change launchSettings.json
....
"launchUrl": "welcome",
"applicationUrl": "http://localhost:5000",
....
and this is my welcome code
app.MapGet("/welcome", () =>
{
var con = "<html><body><h1>Hello!</h1><p> <h3> API Is Ready To Work!!! </h3> </p></body></html>";
return Results.Content(con, "text/html");
});
app.Run();
Related Topics
How to Optionally Turn Off the JSONignore Attribute at Runtime
Garbage Collection When Using Anonymous Delegates for Event Handling
Is There a Reasonable Approach to "Default" Type Parameters in C# Generics
Why Can't I Declare C# Methods Virtual and Static
C# Instantiate Generic List from Reflected Type
Does Using Parameterized SQLcommand Make My Program Immune to SQL Injection
Working with Incredibly Large Numbers in .Net
How to Get the Value of Private Field Using Reflection
Linq Join Iquery, How to Use Defaultifempty
ASP.NET Web Application Message Box
How to Get the Index of the Highest Value in an Array Using Linq
Is the C# Compiler Smart Enough to Optimize This Code
How to Bring Up the Built-In File Copy Dialog
Getting All Possible Combinations from a List of Numbers