千家信息网

.NET 6开发TodoList应用中如何引入数据存储

发表于:2025-11-16 作者:千家信息网编辑
千家信息网最后更新 2025年11月16日,小编给大家分享一下.NET 6开发TodoList应用中如何引入数据存储,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!一.需求作为后端CRUD程序员(bushi,数据存储是开发后端服
千家信息网最后更新 2025年11月16日.NET 6开发TodoList应用中如何引入数据存储

小编给大家分享一下.NET 6开发TodoList应用中如何引入数据存储,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!

    一.需求

    作为后端CRUD程序员(bushi,数据存储是开发后端服务一个非常重要的组件。对我们的TodoList项目来说,自然也需要配置数据存储。

    目前的需求很简单:

    • 需要能持久化TodoList对象并对其进行操作;

    • 需要能持久化TodoItem对象并对其进行操作;

    问题是,我们打算如何存储数据?

    存储组件的选择非常多:以MSSQL Server/Postgres/MySql/SQLite等为代表的关系型数据库,以MongoDB/ElasticSearch等为代表的非关系型数据库,除此之外,我们还可以在开发阶段选择内存数据库,在云上部署的时候还可以选择类似Azure Cosmos DB/AWS DynamoDB以及云上提供的多种关系型数据库。

    应用程序使用数据库服务,一般都是借助成熟的第三方ORM框架,而在.NET后端服务开发的过程中,使用的最多的两个ORM框架应该是:EntityFrameworkCore和Dapper,相比之下,EFCore的使用率更高一些。所以我们也选择EFCore来进行演示。

    二.目标

    在这篇文章中,我们仅讨论如何实现数据存储基础设施的引入,具体的实体定义和操作后面专门来说。

    • 使用MSSQL Server容器作为数据存储组件(前提是电脑上需要安装Docker环境,下载并安装Docker Desktop即可);

    这样选择的理由也很简单,对于使用Mac的小伙伴来说,使用容器来启动MSSQL Server可以避免因为非Windows平台导致的示例无法运行的问题。

    三.原理和思路

    因为我们对开发环境和生产环境的配置有差异,那先来看看共性的部分:

    • 引入EFCore相关的nuget包并进行配置;

    • 添加DbContext对象并进行依赖注入;

    • 修改相关appsettings.{environment}.json文件;

    • 主程序配置。

    • 本地运行MSSQL Server容器并实现数据持久化;

    同上一篇一样,和具体的第三方对接的逻辑我们还是放到Infrastructure里面去,应用程序中只保留对外部服务的抽象操作。

    四.实现

    1. 引入Nuget包并进行配置

    需要在Infrastructure项目中引入以下Nuget包:

    Microsoft.EntityFrameworkCore.SqlServer# 第二个包是用于使用PowerShell命令(Add-Migration/Update-Database/...)需要的,如果使用eftool,可以不安装这个包。Microsoft.EntityFrameworkCore.Tools

    为了使用eftool,需要在Api项目中引入以下Nuget包:

    Microsoft.EntityFrameworkCore.Design

    2. 添加DBContext对象并进行配置#

    在这一步里,我们要添加的是一个具体的DBContext对象,这对于有经验的开发者来说并不是很难的任务。但是在具体实现之前,我们可以花一点时间考虑一下现在的Clean Architecture结构:我们的目的是希望除了Infrastructure知道具体交互的第三方是什么,在Application以及Domain里都要屏蔽底层的具体实现。换言之就是需要在Infrastrcuture之外的项目中使用接口来做具体实现的抽象,那么我们在Application中新建一个Common/Interfaces文件夹用于存放应用程序定义的抽象接口IApplicationDbContext:

    namespace TodoList.Application.Common.Interfaces;public interface IApplicationDbContext{    Task SaveChangesAsync(CancellationToken cancellationToken);}

    接下来在Infrastructure项目中新建Persistence文件夹用来存放和数据持久化相关的具体逻辑,我们在其中定义DbContext对象并实现刚才定义的接口。

    using Microsoft.EntityFrameworkCore;using TodoList.Application.Common.Interfaces;namespace TodoList.Infrastructure.Persistence;public class TodoListDbContext : DbContext, IApplicationDbContext{    public TodoListDbContext(DbContextOptions options) : base(options)    {    }}

    这里的处理方式可能会引起困惑,这个IApplicationDbContext存在的意义是什么。这里的疑问和关于要不要使用Repository模式有关,国外多位大佬讨论过这个问题,即Repository是不是必须的。可以简单理解大家达成的共识是:不是必须的,如果不是有某些特别的数据库访问逻辑,或者有足够的理由需要使用Repository模式,那就保持架构上的简洁,在Application层的多个CQRS Handlers中直接注入该IApplicationDbContext去访问数据库并进行操作。如果需要使用Repository模式,那在这里就没有必要定义这个接口来使用了,Application中只需要定义IRepository,在Infrastructure中实现的BaseRepository中访问DbContext即可。

    我们后面是需要使用Repository的,是因为希望演示最常用的开发模式,但是在这一篇中我保留IApplicationDbConetxt的原因是也希望展示一种不同的实现风格,后面我们还是会专注到Repository上的。

    需要的对象添加好了,下一步是配置DbContext,我们还是遵循当前的架构风格,在Infrastructure项目中添加DependencyInjection.cs文件用于添加所有第三方的依赖:

    using Microsoft.EntityFrameworkCore;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;using TodoList.Application.Common.Interfaces;using TodoList.Infrastructure.Persistence;namespace TodoList.Infrastructure;public static class DependencyInjection{    public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)    {        services.AddDbContext(options =>            options.UseSqlServer(                configuration.GetConnectionString("SqlServerConnection"),                b => b.MigrationsAssembly(typeof(TodoListDbContext).Assembly.FullName)));        services.AddScoped(provider => provider.GetRequiredService());        return services;    }}

    3. 配置文件修改

    我们对appsettings.Development.json文件进行配置:

    {  "Logging": {    "LogLevel": {      "Default": "Information",      "Microsoft.AspNetCore": "Warning"    }  },  "UseFileToLog": true,  "ConnectionStrings": {    "SqlServerConnection": "Server=localhost,1433;Database=TodoListDb;User Id=sa;Password=StrongPwd123;"  }}

    这里需要说明的是如果是使用MSSQL Server默认端口1433的话,连接字符串里是可以不写的,但是为了展示如果使用的不是默认端口应该如何配置,还是显式写在这里了供大家参考。

    4. 主程序配置

    在Api项目中,我们只需要调用上面写好的扩展方法,就可以完成配置。

    var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.ConfigureLog();builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();// 添加基础设施配置builder.Services.AddInfrastructure(builder.Configuration);// 省略以下...

    5. 本地运行MSSQL Server容器及数据持久化

    在保证本地Docker环境正常启动之后,运行以下命令:

    # 拉取mssql镜像$ docker pull mcr.microsoft.com/mssql/server:2019-latest2019-latest: Pulling from mssql/server7b1a6ab2e44d: Already exists 4ffe416cf537: Pull complete fff1d174f64f: Pull complete 3588fd79aff7: Pull complete c8203457909f: Pull complete Digest: sha256:a098c9ff6fbb8e1c9608ad7511fa42dba8d22e0d50b48302761717840ccc26afStatus: Downloaded newer image for mcr.microsoft.com/mssql/server:2019-latestmcr.microsoft.com/mssql/server:2019-latest# 创建持久化存储$ docker create -v /var/opt/mssql --name mssqldata  mcr.microsoft.com/mssql/server:2019-latest /bin/true3c144419db7fba26398aa45f77891b00a3253c23e9a1d03e193a3cf523c66ce1# 运行mssql容器,挂载持久化存储卷$ docker run -d --volumes-from mssqldata --name mssql -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=StrongPwd123' -p 1433:1433 mcr.microsoft.com/mssql/server:2019-latestd99d774f70229f688d71fd13e90165f15abc492aacec48de287d348e047a055e# 确认容器运行状态$ docker psCONTAINER ID   IMAGE                                        COMMAND                  CREATED          STATUS          PORTS                    NAMESd99d774f7022   mcr.microsoft.com/mssql/server:2019-latest   "/opt/mssql/bin/perm…"   24 seconds ago   Up 22 seconds   0.0.0.0:1433->1433/tcp   mssql

    五.验证

    为了验证我们是否可以顺利连接到数据库,我们采用添加Migration并在程序启动时自动进行数据库的Migration方式进行:

    首先安装工具:

    dotnet tool install --global dotnet-ef# dotnet tool update --global dotnet-ef# 生成Migration$ dotnet ef migrations add SetupDb -p src/TodoList.Infrastructure/TodoList.Infrastructure.csproj -s src/TodoList.Api/TodoList.Api.csprojBuild started...Build succeeded.[17:29:15 INF] Entity Framework Core 6.0.1 initialized 'TodoListDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.1' with options: MigrationsAssembly=TodoList.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Done. To undo this action, use 'ef migrations remove'

    为了在程序启动时进行自动Migration,我们向Infrastructure项目中增加一个文件ApplicationStartupExtensions.cs并实现扩展方法:

    using Microsoft.AspNetCore.Builder;using Microsoft.EntityFrameworkCore;using Microsoft.Extensions.DependencyInjection;using TodoList.Infrastructure.Persistence;namespace TodoList.Infrastructure;public static class ApplicationStartupExtensions{    public static void MigrateDatabase(this WebApplication app)    {        using var scope = app.Services.CreateScope();        var services = scope.ServiceProvider;        try        {            var context = services.GetRequiredService();            context.Database.Migrate();        }        catch (Exception ex)        {            throw new Exception($"An error occurred migrating the DB: {ex.Message}");        }    }}

    并在Api项目的Program.cs中调用扩展方法:

    // 省略以上...app.MapControllers();// 调用扩展方法app.MigrateDatabase();app.Run();

    最后运行主程序:

    $ dotnet run --project src/TodoList.ApiBuilding...[17:32:32 INF] Entity Framework Core 6.0.1 initialized 'TodoListDbContext' using provider 'Microsoft.EntityFrameworkCore.SqlServer:6.0.1' with options: MigrationsAssembly=TodoList.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null [17:32:32 INF] Executed DbCommand (22ms) [Parameters=[], CommandType='Text', CommandTimeout='30']SELECT 1[17:32:32 INF] Executed DbCommand (19ms) [Parameters=[], CommandType='Text', CommandTimeout='30']SELECT OBJECT_ID(N'[__EFMigrationsHistory]');[17:32:32 INF] Executed DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']SELECT 1[17:32:32 INF] Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']SELECT OBJECT_ID(N'[__EFMigrationsHistory]');[17:32:33 INF] Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']SELECT [MigrationId], [ProductVersion]FROM [__EFMigrationsHistory]ORDER BY [MigrationId];[17:32:33 INF] Applying migration '20211220092915_SetupDb'.[17:32:33 INF] Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])VALUES (N'20211220092915_SetupDb', N'6.0.1');[17:32:33 INF] Now listening on: https://localhost:7039[17:32:33 INF] Now listening on: http://localhost:5050[17:32:33 INF] Application started. Press Ctrl+C to shut down.[17:32:33 INF] Hosting environment: Development[17:32:33 INF] Content root path: /Users/yu.li1/Projects/asinta/blogs/cnblogs/TodoList/src/TodoList.Api/

    使用数据库工具连接容器数据库,可以看到Migration已经成功地写入数据库表__EFMigrationsHistory了:

    看完了这篇文章,相信你对".NET 6开发TodoList应用中如何引入数据存储"有了一定的了解,如果想了解更多相关知识,欢迎关注行业资讯频道,感谢各位的阅读!

    数据 配置 数据库 存储 项目 开发 容器 对象 文件 运行 程序 应用 选择 接口 方法 模式 环境 第三方 还是 服务 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 华三r690服务器管理地址 人事管理系统如何连接数据库 江西师范的软件开发专业 英国情报机构与网络安全 软件开发销售企业 所得税 促进网络安全产业发展的意见 云南网络安全工程师招聘 手机配件管理软件开发 公安网络安全机测题 中国联通宽带服务器地址 jpa 数据库语句 上海小快互联网科技有限公司 青浦区信息软件开发服务平均价格 河北神玥软件开发 如何获得魔兽怀旧服服务器账号 服务器内存和cpu 立创互联网科技产业园做什么 数据库地址错误原因 网络主权与网络安全的关系 北京ios软件开发性价比高 关系数据库中的外码是什么意思 罪恶都市怎么切换服务器 云服务器本地局域网 服务器管理器如何分配ip 如何关闭笔记本自动连接服务器 人力资源管理功能模块数据库 有线网络无法连接服务器 办公软件数据库设计与引挚 上海网络软件开发定制要多少钱 创建数据库总结
    0