Intro
I have developed ASP.NET Core applications for several years.
But I have little experience developing Console applications of .Net Core.
So in this time, I try Console application.
Can I develop like ASP.NET Core(ex. using DI, outputting logs, etc)?
Environments
- .NET Core ver.5.0.100-preview.7.20366.6
Base project
I create a Console application.
Program.cs
using System;
namespace ConsoleSample
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Loading Config files
I want to use config values like connection strings for connecting Database.
Of course I can load JSON files with StreamReader, etc. But is there any simple way like ASP.NET Core?
Startup.cs
...
private readonly IConfiguration configuration;
public Startup(IHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", false, true)
.AddEnvironmentVariables();
configuration = builder.Build();
}
...
Install
To use ConfigurationBuilder, I installed some packages.
- Microsoft.Extensions.Configuration ver.5.0.0-preview.7.20364.11
- Microsoft.Extensions.Configuration.FileExtensions ver.5.0.0-preview.7.20364.11
- Microsoft.Extensions.Hosting ver.5.0.0-preview.7.20364.11
- Microsoft.Extensions.Configuration.Json ver.5.0.0-preview.7.20364.11
Samples
I add JSON files, and loading functions into Program.cs.
appsettings.json
{
"ConnectionStrings": "Host=localhost;Port=5432;Database=Example;Username=postgres;Password=example"
}
appsettings.Development.json
{
"Message": "Hello Development"
}
appsettings.Production.json
{
"Message": "Hello Production"
}
Program.cs
using System;
using Microsoft.Extensions.Configuration;
namespace ConsoleSample
{
class Program
{
static void Main(string[] args)
{
var config = GetConfiguration();
Console.WriteLine(config["ConnectionStrings"]);
Console.WriteLine("Hello World!");
}
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json", false, true)
.AddEnvironmentVariables();
return builder.Build();
}
}
}
- ConfigurationBuilder Class (Microsoft.Extensions.Configuration) | Microsoft Docs
- FileConfigurationExtensions.SetBasePath(IConfigurationBuilder, String) Method (Microsoft.Extensions.Configuration) | Microsoft Docs
I also can use "System.IO.Directory.GetCurrentDirectory()" to set Base Path.
Get EnvironmentName
I want to load "appsettings.Development.json" for Debug mode.
And When I execute with Release mode, I want to load "appsettings.Production.json".
For ASP.NET Core projects, it may be set EnvironmentName automatically.
So if I set EnvironmentName, I can get EnvironmentName like this.
var environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
Is there more simple way?
So I choose using #if preprocessor directive.
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json", false, true)
#if DEBUG
.AddJsonFile($"appsettings.Development.json", false, true)
#else
.AddJsonFile($"appsettings.Production.json", false, true)
#endif
.AddEnvironmentVariables();
return builder.Build();
}
DI(Dependency Injection)
I also can use DI in Console applications.
Install
- Microsoft.Extensions.DependencyInjection ver.5.0.0-preview.7.20364.11
Samples
IProductService.cs
namespace Products
{
public interface IProductService
{
}
}
ProductService.cs
namespace Products
{
public class ProductService: IProductService
{
}
}
MainController.cs
using System;
using System.Threading.Tasks;
using Products;
namespace Controllers
{
public class MainController
{
private readonly IProductService _product;
public MainController(IProductService product)
{
_product = product;
}
public async Task StartAsync()
{
await Task.Run(() => Console.WriteLine(_product == null));
}
}
}
Program.cs
using System;
using System.Threading.Tasks;
using Controllers;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Products;
namespace ConsoleSample
{
class Program
{
static async Task Main(string[] args)
{
...
var servicesProvider = BuildDi();
using (servicesProvider as IDisposable)
{
var mainController = servicesProvider.GetRequiredService<MainController>();
await mainController.StartAsync();
}
}
...
private static IServiceProvider BuildDi()
{
var services = new ServiceCollection();
services.AddTransient<MainController>();
services.AddScoped<IProductService, ProductService>();
return services.BuildServiceProvider();
}
}
}
Inject IConfiguration
In ASP.NET Core applications, I can use IConfiguration in Controller classes without constructor injection.
But in Console applications, I must add it into IServiceProvider.
Program.cs
...
private static IServiceProvider BuildDi()
{
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(GetConfiguration());
services.AddTransient<MainController>();
services.AddScoped<IProductService, ProductService>();
return services.BuildServiceProvider();
}
...
Now I can get IConfiguration by constructor injection.
MainController.cs
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Products;
namespace Controllers
{
public class MainController
{
private readonly IProductService _product;
public MainController(IConfiguration config,
IProductService product)
{
Console.WriteLine(config["Message"]); // <- print "Hello Development"
_product = product;
}
using Entity Framework Core
As same as ASP.NET Core, I can use Entity Framework Core to access Database.
Install
- Microsoft.EntityFrameworkCore ver.5.0.0-preview.7.20365.15
- Microsoft.EntityFrameworkCore.Relational ver.5.0.0-preview.7.20365.15
- Microsoft.EntityFrameworkCore.Abstractions ver.5.0.0-preview.7.20365.15
- Npgsql.EntityFrameworkCore.PostgreSQL ver.5.0.0-preview7-ci.20200722t163648
ConsoleSampleContext.cs
using Microsoft.EntityFrameworkCore;
namespace Models
{
public class ConsoleSampleContext: DbContext
{
public ConsoleSampleContext(DbContextOptions<ConsoleSampleContext> options)
:base(options)
{
}
}
}
Program.cs
...
private static IServiceProvider BuildDi()
{
var config = GetConfiguration();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);
services.AddDbContext<ConsoleSampleContext>(options =>
options.UseNpgsql(config["ConnectionStrings"]));
services.AddTransient<MainController>();
services.AddScoped<IProductService, ProductService>();
return services.BuildServiceProvider();
}
...
Output logs
I use NLog to output logs.
Install
- NLog ver.4.7.3
- NLog.Extensions.Logging ver.1.6.4
Samples
nlog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<targets>
<target xsi:type="Console" name="outputconsole"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<target xsi:type="File" name="outputfile" fileName="C:\tmp\logs\ConsoleSample\${date:format=yyyy}\${date:format=MM}\${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
</targets>
<rules>
<logger name="*" minlevel="Debug" writeTo="outputconsole" />
<!--Microsoft.* のクラスの Info レベル以下のログはスキップ-->
<logger name="Microsoft.*" maxLevel="Info" final="true" />
<logger name="*" minlevel="Debug" writeTo="outputfile" />
</rules>
</nlog>
ConsoleSample.csproj
...
<ItemGroup>
<Content Include="nlog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="appsettings.Development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="appsettings.Production.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
NLog ver.5.0.0-beta11
I got error with NLog ver.5.0.0-beta11.
Attempt by method 'NLog.Extensions.Logging.ConfigureExtensions.CreateNLogLoggerProvider(System.IServiceProvider, Microsoft.Extensions.Configuration.IConfiguration, NLog.Extensions.Logging.NLogProviderOptions, NLog.LogFactory)' to access method 'NLog.LogManager.get_LogFactory()' failed.
Maybe because some changes of ver.5.0.0.
At least now(2020-08-09), if you want to use these samples. you should use ver.4.7.3.
Top comments (0)