1. 程式人生 > 程式設計 >.NET 6開發TodoList應用引入資料儲存

.NET 6開發TodoList應用引入資料儲存

目錄
  • 一.需求
  • 二.目標
  • 三.原理和思路
  • 四.實現
    • 1. 引入Nuget包並進行配置
    • 2. 新增DBContext物件並進行配置#
    • 3. 配置檔案修改
    • 4. 主程式配置
    • 5. 本地執行MSSQL Server容器及資料持久化
  • 五.驗證

    一.需求

    作為後端CRUD程式設計師(bushi,資料儲存是開發後端服務一個非常重要的元件。對我們的TodoList專案來說,自然也需要配置資料儲存。

    目前的需求很簡單:

    • 需要能持久化TodoList物件並對其進行操作;
    • 需要能持久化TodoItem物件並對其進行操作;

    問題是,我們打算如何儲存資料?

    儲存元件的選擇非常多:以MSSQL Server/Postgres//SQLite

    等為代表的關係型,以MongoDB/ElasticSearch等為代表的非關係型資料庫,除此之外,我們還可以在開發階段選擇記憶體資料庫,在雲上部署的時候還可以選擇類似Azure Cosmos DB/AWS DynamoDB以及雲上提供的多種關係型資料庫。

    應用程式使用資料庫服務,一般都是藉助成熟的第三方ORM框架,而在.NET後端服務開發的過程中,使用的最多的兩個ORM框架應該是:EntityFrameworkCore和Dapper,相比之下,EFCore的使用率更高一些。所以我們也選擇EFCore來進行演示。

    二.目標

    在這篇文章中,我們僅討論如何實現資料儲存基礎設施的引入,具體的實體定義和操作後面專門來說。

    • 使用MSSQL Server容器作為資料儲存元件(前提是電腦上需要安裝環境,下載並安裝Docker Desktop即可);

    這樣選擇的理由也很簡單,對於使用Mac的小夥伴來說,使用容器來啟動MSSQL Server可以避免因為非Windowshttp://www.cppcns.com平臺導致的示例無法執行的問題。

    三.原理和思路

    因為我們對開發環境和生產環境的配置有差異,那先來看看共性的部分:

    • 引入EFCore相關的nuget包並進行配置;
    • 新增DbContext物件並進行依賴注入;
    • 修改相關appsettings.{environment}.on檔案;
    • 主程式配置。
    • 本地執行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<int> 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<TodoListDbContext> options) : base(options)
        {
        }
    }
    
    
    

    這裡的處理方式可能會引起困惑,這個IApplicationDbContext存在的意義是什麼。這裡的疑問和關於要不要使用Repository模式有關,國外多位大佬討論過這個問題,即Repository是不是必須的。可以簡單理解大家達成的共識是:不是必須的,如果不是有某些特別的資料庫訪問邏輯,或者有足夠的理由需要使用Repository模式,那就保持架構上的簡潔,在Application層的多個CQRS Handlers中直接注入該IApplicationDbContext去訪問資料庫並進行操作。如果需要使用Repository模式,那在這裡就沒有必要定義這個介面來使用了,Application中只需要定義IRepository<T>,在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<TodoListDbContext>(options =>
                options.UseSqlServer(
                    configuration.GetConnectionString("SqlServerConnection"),b => b.MigrationsAssembly(typeof(TodoListDbContext).Assembly.FullName)));
    
            services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<TodoListDbContext>());
    
            return services;
        }
    }
    
    

    3. 配置檔案修改

    我們對appsettings.http://www.cppcns.comDevelopment.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-latest
    2019-latest: Pulling from mssql/server
    7b1a6ab2e44d: Already exists 
    4ffe416cf537: Pull complete 
    fff1d174f64f: Pull complete 
    3588fd79aff7: Pull complete 
    c8203457909f: Pull complete 
    Digest: sha256:a098c9ff6fbb8e1c9608ad7511fa42dba8d22e0d50b48302761717840ccc26af
    Status: Downloaded newer image for mcr.microsoft.com/mssql/server:2019-latest
    mcr.microsoft.com/mssql/server:2019-latest
    
    # 建立持久化儲存
    $ docker create -v /var/opt/mssql --name mssqldata  mcr.microsoft.com/mssql/server:2019-latest /bin/true
    3c144419db7fba26398aa45f77891b00a3253c23e9a1d03e193a3cf523c66ce1
    
    # 執行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-latest
    d99d774f70229f688d71fd13e90165f15abc492aacec48de287d348e047a055e
    
    # 確認容器執行狀態
    $ docker ps
    CONTAINER ID   IMAGE                                        COMMAND                  CREATED          STATUS          PORTS                    NAMES
    d99d774f7022   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.csproj
    Build 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<TodoListDbContext>();
                context.Database.Migrate();
            }
            catch (Exception ex)
            {
                throw new Exception($"An error occurred migrating the DB: {ex.Message}");
            }
        }
    }
    
    
    

    並在Api專案的Program.cs中調http://www.cppcns.com用擴充套件方法:

    // 省略以上...
    app.MapControllers();
    
    // 呼叫擴充套件方法
    app.MigrateDatabase();
    
    app.Run();
    
    
    

    最後執行主程式:

    $ dotnet run --project src/TodoList.Api
    Building...
    [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,PublicKeyToken=null 
    [17:32:32 INF] Executed DbCommand (22ms) [Parameters=[],CommandType='Text',CommandTimeout='30']
    SELECT 1
    [17:32:32 INF] Executed DbCommand (19ms) [Parameters=[],CommandTimeout='30']
    SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
    [17:32:32 INF] Executed DbCommand (3ms) [Parameters=[],CommandTimeout='30']
    SELECT 1
    [17:32:32 INF] Executed DbCommand (2ms) [Parameters=[],CommandTimeout='30']
    SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
    [17:32:33 INF] Executed DbCommand (4ms) [Parameters=[],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=[],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應用引入資料儲存

    本篇文章僅完成了資料儲存服務的配置工作,目前還沒有新增任何實體物件和資料庫表定義,所以暫時沒有視覺化的驗證,僅我們可以執行程式看我們的配置是否成功:

    總結:

    在本文中,我們探討並實現瞭如何給.NET 6 Web API專案新增資料儲存服務並進行配置,下一篇開始將會深入資料儲存部分,定義實體,構建Repository模式和SeedData等操作。

    到此這篇關於.NET 6開發TodoList應用引入資料儲存的文章就介紹到這了,更多相關.NET 6開發TodoList應用內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!