Net6 速建控制台应用程序,支持DI,Nlog,DbContext,Options选项模式、windows服务
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
每次搭建NetCore控制台应用程序都得费个十几分钟时间,甚至更多
索性搞个简易版的,供以后复用
1、新建Net6控制台应用程序
1.1、项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" /> <PackageReference Include="NLog.Extensions.Logging" Version="5.0.4" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\CoreDbContext\CoreDbContext.csproj" /> <ProjectReference Include="..\swapCommon\swapCommon.csproj" /> <ProjectReference Include="..\swapModels\swapModels.csproj" /> </ItemGroup> <ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> <None Update="nlog.config"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> </Project>
项目文件中引入的包,可以原封不动直接复制到你的项目文件中
该控制台项目引用了三个外部项目,关于这三个外部项目最后说明
项目中创建了两个配置文件,appsettings.json 和 nlog.config ,分别属于系统配置文件和Nlog日志配置文件
内容分别如下:
appsettings.json 属性需要设置为始终复制

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"virtualPath": "", //调试时为空,swap环境:/dev/swap 正式环境:/swap
"ConnectionStrings": {
"swapDbContext": "Data Source=10.11.12.13;Initial Catalog=swap;Password=xxx;User ID=sa;"
},
"MongoDatabaseName": "swapApiLogsDb",
"MongoDbConnect": "mongodb://127.0.0.1:27017",
"RedisConnectionString": "127.0.0.1:6379,allowadmin=true",
"RedisDbNum": 0,
"IotRedisDbNum": 1,
"UploadFileOptions": {
"FileTypes": ".gif,.jpg,.jpeg,.png,.bmp,.GIF,.JPG,.JPEG,.PNG,.BMP,.xls,.xlsx",
"MaxSize": 104857600,
"BaseUrl": "D:\\dotnet\\fileServer\\WuanManagerFiles",
"BasePath": "Uploads",
"ServerUrl": "https://portal.one5a.com/Uploads"
}
}
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" internalLogLevel="Info" internalLogFile="internal-nlog-AspNetCore.txt"> <!-- enable asp.net core layout renderers --> <extensions> <add assembly="NLog.Web.AspNetCore"/> </extensions> <!-- the targets to write to --> <targets> <!-- File Target for all log messages with basic details --> <target xsi:type="File" name="allfile" fileName="logs/log-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}" archiveAboveSize="10000" maxArchiveFiles="3"/> <!-- File Target for own log messages with extra web details using some ASP.NET core renderers --> <target xsi:type="File" name="ownFile-web" fileName="nlog-AspNetCore-own-${shortdate}.log" layout="${longdate}|${event-properties:item=EventId_Id:whenEmpty=0}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}|${callsite}" archiveAboveSize="10000" maxArchiveFiles="3" /> <!--Console Target for hosting lifetime messages to improve Docker / Visual Studio startup detection --> <target xsi:type="Console" name="lifetimeConsole" layout="${MicrosoftConsoleLayout}" /> </targets> <!-- rules to map from logger name to target --> <rules> <!--All logs, including from Microsoft--> <logger name="*" minlevel="Trace" writeTo="allfile" /> <!--Output hosting lifetime messages to console target for faster startup detection --> <logger name="Microsoft.Hosting.Lifetime" minlevel="Info" writeTo="lifetimeConsole, ownFile-web" final="true" /> <!--Skip non-critical Microsoft logs and so log only own logs (BlackHole) --> <logger name="Microsoft.*" maxlevel="Info" final="true" /> <logger name="System.Net.Http.*" maxlevel="Info" final="true" /> <logger name="*" minlevel="Trace" writeTo="ownFile-web" /> </rules> </nlog>
2、Program 代码

using CoreDbContext; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NLog.Extensions.Logging; using swapModels; namespace CabinetService { static class Program { static void Main(string[] args) { ConfigurationBuilder builder = new ConfigurationBuilder(); builder.AddJsonFile("appsettings.json", true, true); var ConfigRoot = builder.Build();//根节点 IServiceCollection Services = new ServiceCollection(); Services.AddDbContext<swapDbContext>(options => options.UseSqlServer(ConfigRoot.GetConnectionString("swapDbContext")), ServiceLifetime.Scoped); // Services.AddLogging(log => { log.AddConsole(); log.AddNLog(); log.SetMinimumLevel(LogLevel.Information); }); Services.AddScoped<LogService>(); Services.AddScoped<DbTest>(); Services.AddScoped<coreOptions>(); Services.AddOptions().Configure<UploadFileOptions>(A => ConfigRoot.GetSection("UploadFileOptions").Bind(A)); // using (ServiceProvider provider = Services.BuildServiceProvider()) { //测试数据库 var dbTest = provider.GetService<DbTest>(); dbTest.Test(); //选项模式测试 var options = provider.GetService<coreOptions>(); options.Test(); //测试日志 var logservice = provider.GetService<LogService>(); logservice.test(); // } CreateHostBuilder(args).Run(); } public static IHost CreateHostBuilder(string[] args) { var builder = Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { services.AddHostedService<IotService>(); }).UseWindowsService(); var host = builder.Build(); return host; } } /// <summary> /// efCore first 测试 /// </summary> public class DbTest { private readonly swapDbContext context; public DbTest(swapDbContext context) { this.context = context; } public void Test() { var linq = context.Users.Where(A => A.uid != ""); var sql = linq.ToQueryString(); Console.WriteLine("linq转化为的SQL语句为:" + sql); var data = linq.ToList(); foreach (var item in data) { Console.WriteLine(item.userName); } } } public class coreOptions { private readonly IOptionsSnapshot<UploadFileOptions> options; public coreOptions(IOptionsSnapshot<UploadFileOptions> options) { this.options = options; } public void Test() { Console.WriteLine("========================================"); Console.WriteLine(options.Value.BaseUrl); Console.WriteLine(options.Value.MaxSize); Console.WriteLine(options.Value.FileTypes); } } public class LogService { private readonly ILogger<LogService> logger; public LogService(ILogger<LogService> logger) { this.logger = logger; } public void test() { for (int i = 0; i < 10; i++) { logger.LogTrace("跟踪"); logger.LogDebug("调试"); logger.LogInformation("信息"); logger.LogError("错误"); logger.LogCritical("严重错误"); try { int a = 0; int b = 1; var c = b / a; } catch (Exception ex) { logger.LogError(ex, "异常"); } } } /// <summary> /// windows 日志 部署在linux 下无效 /// </summary> public void EventLog() { //Microsoft.Extensions.Logging.EventLog } } }
2.1、选项模式中用到 swapModels 中定义的实体类,如下

public class UploadFileOptions { /// <summary> /// 允许的文件类型 /// </summary> public string FileTypes { get; set; } /// <summary> /// 最大文件大小 /// </summary> public int MaxSize { get; set; } /// <summary> /// 文件的基地址 /// </summary> public string BaseUrl { get; set; } /// <summary> /// IIS 虚拟路径 解析到 BaseUrl /// </summary> public string BasePath { get; set; } public string ServerUrl { get; set; } }
2.2、DbContext 数据库上下文
根据项目文件可知,该控制台应用程序引用项目CoreDbContext ,CoreDbContext 采用EFCORE+CodeFirst的模式进行搭建,下面简单贴出部分代码
swapDbContext

using CoreDbContext.DbDtos; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; namespace CoreDbContext { /// <summary> /// add-Migration -Context 可以通过-Context 指定上下文,因此,项目中可以有多个DbCOntext,这样就支持多个数据库 /// </summary> public class swapDbContext : DbContext { public DbSet<T_Group> Groups { get; set; } public DbSet<T_AppInfo> AppInfos { get; set; } public DbSet<T_User> Users { get; set; } public DbSet<T_Role> Roles { get; set; } public DbSet<T_UserRole> UserRoles { get; set; } public DbSet<T_Station> Stations { get; set; } public DbSet<T_SysDictionary> SysDictionaries { get; set; } public DbSet<T_CabinetProduce> CabinetProduces { get; set; } public DbSet<T_Cabinet> Cabinets { get; set; } public DbSet<T_BatteryProduce> BatteryProduces { get; set; } public DbSet<T_Battery> Batteries { get; set; } public DbSet<C_Customer> Customers { get; set; } public DbSet<C_CustomerBattery> CustomerBatteries { get; set; } public DbSet<C_ChargeGoods> ChargeGoods { get; set; } public DbSet<C_ChargeGoodOrder> ChargeGoodOrders { get; set; } public DbSet<C_ChargeGoodOrderDtl> ChargeGoodOrderDtls { get; set; } public DbSet<C_Deposit> Deposits { get; set; } public DbSet<C_DepositOrder> DepositOrders { get; set; } public DbSet<C_DepositApply> DepositApplies { get; set; } public DbSet<T_Document> Documents { get; set; } public DbSet<T_SysParam> SysParams { get; set; } public swapDbContext() : base() { } public swapDbContext(DbContextOptions<swapDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { //关闭级联删除 var foreignKeys = modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()).Where(fk => fk.DeleteBehavior == DeleteBehavior.Cascade); foreach (var fk in foreignKeys) { fk.DeleteBehavior = DeleteBehavior.Restrict; } //建立索引 并 增加唯一性约束 modelBuilder.Entity<T_CabinetProduce>().HasIndex(u => u.batchNo).IsUnique(); base.OnModelCreating(modelBuilder); //从当前程序集命名空间加载所有的IEntityTypeConfiguration modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } } /// <summary> /// 开发环境专用 用于add-Migration 时使用 /// </summary> public class swapDbContextFactory : IDesignTimeDbContextFactory<swapDbContext> { public swapDbContext CreateDbContext(string[] args) { var optionsBuilder = new DbContextOptionsBuilder<swapDbContext>(); optionsBuilder.UseSqlServer("Data Source=1.2.3.4;Initial Catalog=swap;Password=xxx;User ID=sa;"); return new swapDbContext(optionsBuilder.Options); } } }
dto_User

/// <summary> /// 用于平台登录 /// </summary> public class T_User { public string? uid { get; set; } public string? userName { get; set; } public string? passWord { get; set; } public bool isEnable { get; set; } = true; public DateTime? lastLoginTime { get; set; } public DateTime createTime { get; set; } public string? groupId { get; set; } public bool isManager { get; set; } /// <summary> /// 一个用户只能属于一个商户 /// </summary> public virtual T_Group? Group { get; set; } }
User_Config

using CoreDbContext.DbDtos; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using System; using System.Collections.Generic; using System.Text; namespace CoreDbContext.DbConfigs { internal class T_UserConfig : IEntityTypeConfiguration<T_User> { public void Configure(EntityTypeBuilder<T_User> builder) { builder.ToTable("T_Users"); builder.HasKey(A => A.uid); builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主键"); builder.Property(A => A.userName).HasColumnType("varchar(64)").HasComment("登录账户"); builder.Property(A => A.passWord).HasColumnType("varchar(512)").HasComment("密码,密文"); builder.Property(A => A.groupId).HasColumnType("varchar(64)").HasComment("所属商户"); builder.Property(A => A.isManager) .HasComment("是否是平台管理员,否为商家端登录用户"); //builder.HasQueryFilter(A => A.isEnable == true); //一对多主外键配置 builder.HasOne<T_Group>(A => A.Group).WithMany().HasForeignKey(A => A.groupId); } } }
项目CoreDbContext 的项目文件为:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.6"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> </Project>
2.3、windows服务部分

public class IotService : BackgroundService { System.Timers.Timer timer1; public override Task StartAsync(CancellationToken cancellationToken) { if (!cancellationToken.IsCancellationRequested) { timer1 = new System.Timers.Timer(); timer1.Interval = 1000 * 1; //设置计时器事件间隔执行时间 每隔1秒执行一次 timer1.Elapsed += new System.Timers.ElapsedEventHandler(timer1_Elapsed); timer1.Enabled = true; } return base.StartAsync(cancellationToken); } /// <summary> /// 每一秒执行一次 /// </summary> /// <param name="stoppingToken"></param> /// <returns></returns> protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { var now = DateTime.Now; Console.WriteLine("ExecuteAsync执行结果:" + now); await Task.Delay(1000, stoppingToken); } // if (!stoppingToken.IsCancellationRequested) { //只执行一次 var now = DateTime.Now; Console.WriteLine("ExecuteAsync执行结果:" + now); await Task.Delay(1000, stoppingToken); } if (stoppingToken.IsCancellationRequested) { //不使用ExecuteAsync ,使用自定义的timer1 var now = DateTime.Now; Console.WriteLine("ExecuteAsync执行结果:" + now); await Task.Delay(1000, stoppingToken); } } public override Task StopAsync(CancellationToken cancellationToken) { timer1.Enabled = false; return base.StopAsync(cancellationToken); } private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { var now = DateTime.Now; Console.WriteLine("timer1_Elapsed执行结果:" + now); } }
关于windows服务NetCore部分,可参考:
关于code first 搭建可参考鄙人的博客
Net6/NetCore3.1搭建codeFirst 【支持多dbcontext】并接合TransactionScope 完成事务操作
Net5 控制台应用程序引入EFCore+CodeFirst
Net5 EFCore CodeFirst FluentApi关系配置
Net5 EfCore CodeFirst 乐观锁 RowRersion
3、swapCommon 公共方法类库
这个类库没用到哦
@天才卧龙的博客
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端