資料訪問 - EntityFramework整合

磊_磊 發表於 2022-07-05

前言

Masa提供了基於EntityFramework的資料整合,並提供了資料過濾與軟刪除的功能,下面我們將介紹如何使用它?

MasaDbContext入門

  1. 新建ASP.NET Core 空專案Assignment.MasaEntityFramework,並安裝Masa.Contrib.Data.EntityFrameworkCoreSwashbuckle.AspNetCoreMicrosoft.EntityFrameworkCore.InMemoryMicrosoft.EntityFrameworkCore.Tools

    dotnet add package Masa.Contrib.Data.EntityFrameworkCore --version 0.4.0-rc.4
    dotnet add package Swashbuckle.AspNetCore --version 6.2.3
    dotnet add package Microsoft.EntityFrameworkCore.InMemory --version 6.0.5
    dotnet add package Microsoft.EntityFrameworkCore.Tools --version 6.0.5
    

    安裝Swashbuckle.AspNetCore是為了方便通過Swagger來操作服務
    安裝Microsoft.EntityFrameworkCore.InMemory是為了方便,因此使用記憶體資料庫,如果需要使用其他資料庫,請自行安裝對應的包
    安裝Microsoft.EntityFrameworkCore.Tools是為了使用CodeFirst建立資料庫

  2. 新建類User

    public class User
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public uint Gender { get; set; }
    
        public DateTime BirthDay { get; set; }
    
        public DateTime CreationTime { get; set; }
    
        public User()
        {
            this.CreationTime = DateTime.Now;
        }
    }
    
  3. 新建使用者上下文UserDbContext.cs

    public class UserDbContext : MasaDbContext
    {
        public DbSet<User> User { get; set; }
    
        public UserDbContext(MasaDbContextOptions options) : base(options)
        {
        }
    }
    

    UserDbContext改為繼承MasaDbContext, 並新增一個引數的建構函式,引數型別為MasaDbContextOptions
    當專案中存在多個DbContext時,需要改為繼承MasaDbContext<TDbContext>,建構函式引數型別改為MasaDbContext<TDbContext>

  4. 新建類AddUserRequest作為新增使用者的引數

    public class AddUserRequest
    {
        public string Name { get; set; }
    
        public uint Gender { get; set; }
    
        public DateTime BirthDay { get; set; }
    }
    
  5. 新建類HostExtensions用於遷移資料庫(使用CodeFirst)

    public static class HostExtensions
    {
        public static void MigrateDbContext<TContext>(
            this IHost host, Action<TContext, IServiceProvider> seeder) where TContext : DbContext
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                var context = services.GetRequiredService<TContext>();
                context.Database.EnsureCreated();
                seeder(context, services);
            }
        }
    }
    
  6. 修改Program.cs,新增Swagger支援

    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    
    var app = builder.Build();
    
    app.UseSwagger();
    app.UseSwaggerUI();
    

不需要Swagger可不新增,使用Swagger僅僅是為了測試呼叫服務,使用Postman或其他的Http工具也可以

  1. 修改Program.cs,新增使用者上下文(重點)

    builder.Services.AddMasaDbContext<UserDbContext>(options => 
    {
      options.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseInMemoryDatabase("test")
    });
    
  2. 修改Program.cs,使專案支援CodeFirst

    app.MigrateDbContext<UserDbContext>((context, services) =>
    {
    });
    

    不需要CodeFirst,不支援程式碼生成資料庫可不新增

  3. 測試MasaDbContext,修改Program.cs

    app.MapPost("/add", (UserDbContext dbContext, [FromBody] AddUserRequest request) =>
    {
        dbContext.Set<User>().Add(new User()
        {
            Name = request.Name,
            Gender = request.Gender,
            BirthDay = request.BirthDay
        });
        dbContext.SaveChanges();
    });
    
    app.MapGet("/list", (UserDbContext dbContext) =>
    {
        return dbContext.Set<User>().ToList();
    });
    

    自行執行專案,執行add後建立一個新的使用者,之後執行list得到一個以上的使用者資料,則證明MasaDbContext使用無誤

如何使用軟刪除

  1. 選中Assignment.MasaEntityFramework並安裝Masa.Contrib.Data.Contracts.EF

    dotnet add package Masa.Contrib.Data.Contracts.EF --version 0.4.0-rc.4
    
  2. 修改類User,並實現ISoftDelete,程式碼改為:

    public class User : ISoftDelete//重點:改為實現ISoftDelete
    {
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public uint Gender { get; set; }
    
        public DateTime BirthDay { get; set; }
    
        public DateTime CreationTime { get; set; }
    
        public bool IsDeleted { get; private set; }
    
        public User()
        {
            this.CreationTime = DateTime.Now;
        }
    }
    

    增加實現ISoftDelete,併為IsDeleted屬性新增set支援(可以是private set;)

  3. 修改Program.cs,並啟用資料過濾

    builder.Services.AddMasaDbContext<UserDbContext>(options =>
    {
        options.Builder = (_, dbContextOptionsBuilder) => dbContextOptionsBuilder.UseInMemoryDatabase("test");
        options.UseFilter();//啟用資料過濾,完整寫法:options.UseFilter(filterOptions => filterOptions.EnableSoftDelete = true);
    });
    
  4. 測試軟刪除是否成功

  • 修改Program.cs,新增刪除方法

    app.MapDelete("/delete", (UserDbContext dbContext, int id) =>
    {
        var user = dbContext.Set<User>().First(u => u.Id == id);
        dbContext.Set<User>().Remove(user);
        dbContext.SaveChanges();
    });
    

最後,先呼叫add方法建立使用者後,之後再呼叫list方法獲取所有的使用者列表,並取出任意一條id資訊,然後再呼叫delete方法刪除使用者,最後再呼叫list方法,檢視取出的id是否存在,以此來驗證軟刪除是否有效。

如何臨時禁用軟刪除過濾

預設查詢中會將標記已經被刪除的資料過濾不再進行查詢,但也有一些場景需要查詢所有的資料,此時就需要用到資料過濾IDataFilter

  1. 新增All方法用於查詢所有的資料(包含標記已經刪除的資料)

    app.MapGet("/all", (UserDbContext dbContext, [FromServices] IDataFilter dataFilter) =>
    {
        //通過DI獲取到IDataFilter,並呼叫其Disable方法可臨時禁用ISoftDelete條件過濾
        using (dataFilter.Disable<ISoftDelete>())
        {
            return dbContext.Set<User>().ToList();
        }
    });
    
  2. 重新執行專案,重複執行驗證軟刪除步驟,確保通過list方法訪問不到資料

    重複執行驗證軟刪除步驟的原因在於本示例使用的是記憶體資料庫,專案停止後,所有資料都會被清空,重新執行是為了確保資料存在,僅被標記為刪除

  3. 執行all方法,獲取所有的資料,檢視id所對應的使用者資料是否存在

從配置檔案中獲取資料庫連線字串

  1. 選中專案Assignment.MasaEntityFramework,並安裝Masa.Contrib.Data.EntityFrameworkCore.InMemory

    dotnet add package Masa.Contrib.Data.EntityFrameworkCore.InMemory --version 0.4.0-rc.4
    

    根據需要安裝對應資料庫包即可,如:Masa.Contrib.Data.EntityFrameworkCore.SqlServer (SqlServer)、Masa.Contrib.Data.EntityFrameworkCore.Pomelo.MySql (Pomelo提供的MySql)、Masa.Contrib.Data.EntityFrameworkCore.Oracle (Oracle)等

  2. 修改Program.cs,調整新增使用者上下文配置為:

    builder.Services.AddMasaDbContext<UserDbContext>(options => options.UseInMemoryDatabase().UseFilter());
    
  3. 修改appsettings.json,增加使用者資料庫連線字串:

    {
      "ConnectionStrings": {
        "DefaultConnection": "test"//更換為指定的資料庫連線字串
      }
    }
    
  4. 修改Program.cs,新增database方法,驗證當前資料庫是test

    app.MapGet("/database", (UserDbContext dbContext) =>
    {
        var field = typeof(MasaDbContext).GetField("Options", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic)!;
        var masaDbContextOptions = field.GetValue(dbContext) as MasaDbContextOptions;
        foreach (var dbContextOptionsExtension in masaDbContextOptions!.Extensions)
        {
            if (dbContextOptionsExtension is InMemoryOptionsExtension memoryOptionsExtension)
            {
                return memoryOptionsExtension.StoreName;
            }
        }
    
        return "";
    });
    

最後訪問http://localhost:5002/database,驗證當前的資料庫名稱與修改後的資料庫名稱是否一致
驗證資料庫為test

常見問題

  • 如何更改預設讀取的配置節點?
  1. 修改使用者上下文UserDbContext並增加ConnectionStringName特性:

    [ConnectionStringName("User")]//自定義節點名
    public class UserDbContext : MasaDbContext
    {
        public DbSet<User> User { get; set; }
    
        public UserDbContext(MasaDbContextOptions options) : base(options)
        {
        }
    }
    
  2. 修改配置appsettings.json

    {
      "ConnectionStrings": {
        "User": "test"//改為從User節點讀取資料庫連線字串
      }
    }
    
  • 除了從配置檔案中獲取,還支援從其他地方獲取資料庫連線字串嗎?

目前有兩種辦法可以更改資料庫連線字串。

方法1: 修改Program.cs,並刪除appsettings.json資料庫連線字串的配置

  1. 修改Program.cs

    builder.Services.Configure<MasaDbConnectionOptions>(option =>
    {
        option.ConnectionStrings = new ConnectionStrings(new List<KeyValuePair<string, string>>()
        {
            new("User", "test2")//其中鍵為節點名,與ConnectionStringName特性的Name值保持一致即可,如果未指定ConnectionStringName,則應該為DefaultConnection,值為資料庫連線字串
        });
    });
    
  2. 修改appsettings.json配置

    //  "ConnectionStrings": {
    //    "User": "test"
    //  },
    
  3. 呼叫database方法,驗證當前資料庫是否為test2

驗證資料庫為test2

方法2: 重寫IConnectionStringProviderIDbConnectionStringProvider的實現並新增到DI中

  1. 新建類CustomizeConnectionStringProvider

    public class CustomizeConnectionStringProvider : IConnectionStringProvider
    {
        public Task<string> GetConnectionStringAsync(string name = "DefaultConnection") => Task.FromResult    (GetConnectionString(name));
    
        public string GetConnectionString(string name = "DefaultConnection") => "test3";
    }
    
  2. 新建類CustomizeDbConnectionStringProvider

    public class CustomizeDbConnectionStringProvider : IDbConnectionStringProvider
    {
        public List<MasaDbContextConfigurationOptions> DbContextOptionsList { get; } = new()
        {
            new MasaDbContextConfigurationOptions("test3")
        };
    }
    
  3. 修改Program.cs

    builder.Services.AddSingleton<IConnectionStringProvider,CustomizeConnectionStringProvider>();
    builder.Services.AddSingleton<IDbConnectionStringProvider,CustomizeDbConnectionStringProvider>();
    
  4. 呼叫database方法,驗證當前資料庫是否為test3
    驗證資料庫為test3

總結

本篇文章主要講解了MasaDbContext的基本用法以及軟刪除、資料過濾如何使用,下篇文章我們會講解一下MasaDbContext是如何實現軟刪除、資料過濾的,以及本篇文章中提到使用資料庫時不指定資料庫連結字串時如何實現的

本章原始碼

Assignment05

https://github.com/zhenlei520/MasaFramework.Practice

開源地址

MASA.BuildingBlocks:https://github.com/masastack/MASA.BuildingBlocks

MASA.Contrib:https://github.com/masastack/MASA.Contrib

MASA.Utils:https://github.com/masastack/MASA.Utils

MASA.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

如果你對我們的 MASA Framework 感興趣,無論是程式碼貢獻、使用、提 Issue,歡迎聯絡我們

16373211753064.png