基于NetCorePal Cloud Framework的DDD架構(gòu)管理系統(tǒng)實(shí)踐指南
前段時(shí)間在做一個(gè)管理系統(tǒng)的項(xiàng)目,想嘗試一下DDD架構(gòu)在實(shí)際項(xiàng)目中的應(yīng)用。經(jīng)過(guò)一番調(diào)研,最終選擇了NetCorePal Cloud Framework作為基礎(chǔ)框架,結(jié)合.NET 10和Vue 3搭建了一套完整的前后端分離架構(gòu)。今天就想和大家分享一下這個(gè)項(xiàng)目的架構(gòu)設(shè)計(jì)和技術(shù)選型,希望能給正在做類似項(xiàng)目的朋友一些參考。
項(xiàng)目源碼地址:https://github.com/zhouda1fu/Ncp.Admin
項(xiàng)目概述
這個(gè)項(xiàng)目是一個(gè)典型的企業(yè)級(jí)管理系統(tǒng),包含了用戶、角色、部門等基礎(chǔ)功能模塊。在技術(shù)選型上,采用了目前比較主流的技術(shù)棧:
后端方面,使用.NET 10作為主要框架,配合EF Core做數(shù)據(jù)訪問(wèn),F(xiàn)astEndpoints替代傳統(tǒng)的Controller,MediatR實(shí)現(xiàn)CQRS模式。數(shù)據(jù)存儲(chǔ)支持MySQL、PostgreSQL和SQL Server,消息隊(duì)列選擇了RabbitMQ(通過(guò)CAP框架集成),緩存用Redis,還集成了.NET Aspire來(lái)做云原生的基礎(chǔ)設(shè)施管理。
前端部分基于Vben Admin,這是一個(gè)非常優(yōu)秀的Vue 3 + TypeScript + Vite的管理后臺(tái)模板,UI組件用的是Ant Design Vue,整體體驗(yàn)不錯(cuò)。
架構(gòu)設(shè)計(jì)
分層架構(gòu)
整個(gè)項(xiàng)目采用了經(jīng)典的三層架構(gòu),這個(gè)結(jié)構(gòu)應(yīng)該很多做DDD的朋友都比較熟悉。三層之間的依賴關(guān)系是單向的:Web層依賴Infrastructure層,Infrastructure層依賴Domain層,Domain層作為核心,不依賴任何其他層。
Ncp.Admin
├── Domain(領(lǐng)域?qū)樱?
│ ├── AggregatesModel(聚合模型)
│ └── DomainEvents(領(lǐng)域事件)
├── Infrastructure(基礎(chǔ)設(shè)施層)
│ ├── EntityConfigurations(實(shí)體配置)
│ └── Repositories(倉(cāng)儲(chǔ)實(shí)現(xiàn))
└── Web(表現(xiàn)層)
├── Application(應(yīng)用服務(wù)層)
│ ├── Commands(命令)
│ ├── Queries(查詢)
│ └── DomainEventHandlers(領(lǐng)域事件處理器)
└── Endpoints(API端點(diǎn))這種分層的好處是職責(zé)清晰,Domain層只關(guān)注業(yè)務(wù)邏輯,Infrastructure層負(fù)責(zé)技術(shù)實(shí)現(xiàn),Web層處理HTTP請(qǐng)求和響應(yīng)。
核心設(shè)計(jì)模式
1. 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)
在這個(gè)項(xiàng)目中,DDD主要體現(xiàn)在聚合根的設(shè)計(jì)上。每個(gè)聚合根都有自己的業(yè)務(wù)邊界,狀態(tài)只能通過(guò)業(yè)務(wù)方法來(lái)修改。就拿部門這個(gè)聚合根來(lái)說(shuō)吧:
/// <summary>
/// 部門ID(強(qiáng)類型ID)
/// </summary>
public partial record DeptId : IInt64StronglyTypedId;
/// <summary>
/// 部門聚合根
/// </summary>
public class Dept : Entity<DeptId>, IAggregateRoot
{
public string Name { get; private set; } = string.Empty;
public string Remark { get; private set; } = string.Empty;
public DeptId ParentId { get; private set; } = default!;
public int Status { get; private set; } = 1;
protected Dept() { }
// 業(yè)務(wù)方法:更新部門信息
public void UpdateInfo(string name, string remark, DeptId parentId, int status)
{
Name = name;
Remark = remark;
ParentId = parentId;
Status = status;
UpdateTime = new UpdateTime(DateTimeOffset.UtcNow);
// 發(fā)布領(lǐng)域事件
AddDomainEvent(new DeptInfoChangedDomainEvent(this));
}
// 軟刪除
public void SoftDelete()
{
if (IsDeleted)
{
throw new KnownException("部門已經(jīng)被刪除");
}
IsDeleted = true;
UpdateTime = new UpdateTime(DateTimeOffset.UtcNow);
}
}這里有幾個(gè)設(shè)計(jì)點(diǎn)我覺得值得說(shuō)一下。首先是強(qiáng)類型ID,比如DeptId,這樣可以避免把部門ID和用戶ID搞混,編譯器就能幫你檢查出來(lái)。其次是屬性都用private set,外面不能直接修改,必須通過(guò)業(yè)務(wù)方法,這樣就保證了業(yè)務(wù)規(guī)則的一致性。另外,當(dāng)部門信息變更時(shí)會(huì)發(fā)布領(lǐng)域事件,這樣可以通知其他需要同步更新的地方,比如用戶表中的部門名稱。
2. CQRS模式(命令查詢職責(zé)分離)
CQRS在這個(gè)項(xiàng)目中主要體現(xiàn)在讀寫分離上。寫操作通過(guò)命令(Command)來(lái)處理,讀操作通過(guò)查詢(Query)來(lái)處理。這樣做的好處是職責(zé)清晰,而且可以針對(duì)不同的場(chǎng)景做優(yōu)化。
寫操作這邊,命令的定義很簡(jiǎn)單,就是一個(gè)record。每個(gè)命令都有對(duì)應(yīng)的驗(yàn)證器和處理器??匆粋€(gè)創(chuàng)建部門的例子:
/// <summary>
/// 創(chuàng)建部門命令
/// </summary>
public record CreateDeptCommand(string Name, string Remark, DeptId? ParentId, int Status)
: ICommand<DeptId>;
/// <summary>
/// 命令驗(yàn)證器
/// </summary>
public class CreateDeptCommandValidator : AbstractValidator<CreateDeptCommand>
{
public CreateDeptCommandValidator(DeptQuery deptQuery)
{
RuleFor(d => d.Name).NotEmpty().WithMessage("部門名稱不能為空");
RuleFor(d => d.Name)
.MustAsync(async (n, ct) => !await deptQuery.DoesDeptExist(n, ct))
.WithMessage(d => $"該部門已存在,Name={d.Name}");
RuleFor(d => d.Status).InclusiveBetween(0, 1).WithMessage("狀態(tài)值必須為0或1");
}
}
/// <summary>
/// 命令處理器
/// </summary>
public class CreateDeptCommandHandler(IDeptRepository deptRepository)
: ICommandHandler<CreateDeptCommand, DeptId>
{
public async Task<DeptId> Handle(CreateDeptCommand request, CancellationToken cancellationToken)
{
var parentId = request.ParentId ?? new DeptId(0);
var dept = new Dept(request.Name, request.Remark, parentId, request.Status);
await deptRepository.AddAsync(dept, cancellationToken);
// 注意:不需要手動(dòng)調(diào)用SaveChanges,框架會(huì)自動(dòng)處理
return dept.Id;
}
}驗(yàn)證器這里用了FluentValidation,支持同步和異步驗(yàn)證。比如檢查部門名稱是否已存在這種需要查數(shù)據(jù)庫(kù)的驗(yàn)證,就可以用異步的MustAsync。
讀操作這邊,直接使用DbContext,而且可以用投影來(lái)優(yōu)化性能。比如獲取部門樹的時(shí)候,只選擇需要的字段:
/// <summary>
/// 部門查詢服務(wù)
/// </summary>
public class DeptQuery(ApplicationDbContext applicationDbContext) : IQuery
{
private DbSet<Dept> DeptSet { get; } = applicationDbContext.Depts;
/// <summary>
/// 獲取部門樹(使用投影優(yōu)化性能)
/// </summary>
public async Task<IEnumerable<DeptTreeDto>> GetDeptTreeAsync(
bool includeInactive = false,
CancellationToken cancellationToken = default)
{
// 使用投影只選擇需要的字段,減少內(nèi)存占用
var allDepts = await DeptSet.AsNoTracking()
.WhereIf(!includeInactive, d => d.Status != 0)
.Select(d => new DeptTreeNode
{
Id = d.Id,
Name = d.Name,
Remark = d.Remark,
ParentId = d.ParentId,
Status = d.Status,
CreatedAt = d.CreatedAt
})
.ToListAsync(cancellationToken);
// 在內(nèi)存中構(gòu)建樹形結(jié)構(gòu)
return BuildTreeStructure(allDepts);
}
}這樣讀寫分離的好處是,查詢這邊可以針對(duì)不同的查詢場(chǎng)景做優(yōu)化,比如用投影減少內(nèi)存占用,或者將來(lái)可以加緩存、用讀庫(kù)等,而不會(huì)影響寫操作的邏輯。
3. 事件驅(qū)動(dòng)架構(gòu)
事件驅(qū)動(dòng)這塊,項(xiàng)目實(shí)現(xiàn)了領(lǐng)域事件和集成事件兩種機(jī)制。領(lǐng)域事件主要用于聚合內(nèi)部的同步操作,集成事件用于跨服務(wù)通信。
比如說(shuō),當(dāng)部門信息變更的時(shí)候,需要同步更新用戶表中的部門名稱。這個(gè)過(guò)程就可以通過(guò)領(lǐng)域事件來(lái)實(shí)現(xiàn):
/// <summary> /// 部門信息變更領(lǐng)域事件 /// </summary> public record DeptInfoChangedDomainEvent(Dept Dept) : IDomainEvent;
然后在事件處理器中處理這個(gè)邏輯:
/// <summary>
/// 部門信息變更領(lǐng)域事件處理器 - 用于更新用戶部門名稱
/// </summary>
public class DeptInfoChangedDomainEventHandlerForUpdateUserDeptName(
IMediator mediator,
UserQuery userQuery)
: IDomainEventHandler<DeptInfoChangedDomainEvent>
{
public async Task Handle(DeptInfoChangedDomainEvent domainEvent, CancellationToken cancellationToken)
{
var dept = domainEvent.Dept;
var deptId = dept.Id;
var newDeptName = dept.Name;
// 查詢所有屬于該部門的用戶ID
var userIds = await userQuery.GetUserIdsByDeptIdAsync(deptId, cancellationToken);
// 通過(guò)Command更新每個(gè)用戶的部門名稱(而不是直接操作數(shù)據(jù)庫(kù))
foreach (var userId in userIds)
{
var command = new UpdateUserDeptNameCommand(userId, newDeptName);
await mediator.Send(command, cancellationToken);
}
}
}這樣設(shè)計(jì)的好處是,部門聚合和用戶聚合之間沒有直接依賴,通過(guò)事件來(lái)通信。如果將來(lái)需要增加新的業(yè)務(wù)邏輯,比如部門變更時(shí)要發(fā)送通知,只需要再加一個(gè)事件處理器就行了,不需要改現(xiàn)有的代碼。
4. FastEndpoints輕量級(jí)API框架
在API設(shè)計(jì)這塊,項(xiàng)目選擇了FastEndpoints而不是傳統(tǒng)的Controller。主要是覺得FastEndpoints的代碼更簡(jiǎn)潔,性能也更好。一個(gè)端點(diǎn)就是一個(gè)類,職責(zé)清晰。
看一個(gè)創(chuàng)建部門的例子:
/// <summary>
/// 創(chuàng)建部門的API端點(diǎn)
/// </summary>
[Tags("Depts")]
public class CreateDeptEndpoint(IMediator mediator)
: Endpoint<CreateDeptRequest, ResponseData<CreateDeptResponse>>
{
public override void Configure()
{
Post("/api/admin/dept");
AuthSchemes(JwtBearerDefaults.AuthenticationScheme);
Permissions(PermissionCodes.AllApiAccess, PermissionCodes.DeptCreate);
}
public override async Task HandleAsync(CreateDeptRequest req, CancellationToken ct)
{
var cmd = new CreateDeptCommand(req.Name, req.Remark, req.ParentId, req.Status);
var deptId = await mediator.Send(cmd, ct);
var response = new CreateDeptResponse(deptId, req.Name, req.Remark);
await Send.OkAsync(response.AsResponseData(), cancellation: ct);
}
}代碼很簡(jiǎn)潔,一個(gè)類就把路由、認(rèn)證、權(quán)限都配置好了。請(qǐng)求和響應(yīng)都是強(qiáng)類型的,類型安全有保障。而且測(cè)試起來(lái)也很方便,不需要啟動(dòng)HTTP服務(wù)器,直接測(cè)端點(diǎn)就行了。
幾個(gè)核心特性
1. 強(qiáng)類型ID
這個(gè)項(xiàng)目里所有聚合根都用強(qiáng)類型ID,而不是直接用long或int。比如部門ID是DeptId,用戶ID是UserId。這樣做的好處是編譯器能幫你檢查類型錯(cuò)誤,不會(huì)把部門ID和用戶ID搞混。
使用起來(lái)也很簡(jiǎn)單:
// 定義強(qiáng)類型ID public partial record DeptId : IInt64StronglyTypedId; // 使用強(qiáng)類型ID var deptId = new DeptId(123); var parentId = request.ParentId ?? new DeptId(0);
框架會(huì)自動(dòng)處理序列化和類型轉(zhuǎn)換,用起來(lái)很順手。
2. 倉(cāng)儲(chǔ)模式
倉(cāng)儲(chǔ)這塊,寫操作通過(guò)倉(cāng)儲(chǔ)來(lái)處理,查詢操作直接使用DbContext。倉(cāng)儲(chǔ)的實(shí)現(xiàn)很簡(jiǎn)單:
/// <summary>
/// 部門倉(cāng)儲(chǔ)接口
/// </summary>
public interface IDeptRepository : IRepository<Dept, DeptId> { }
/// <summary>
/// 部門倉(cāng)儲(chǔ)實(shí)現(xiàn)
/// </summary>
public class DeptRepository(ApplicationDbContext context)
: RepositoryBase<Dept, DeptId, ApplicationDbContext>(context),
IDeptRepository { }框架會(huì)自動(dòng)管理事務(wù)和SaveChanges,命令處理器里不需要手動(dòng)調(diào)用,這樣代碼更簡(jiǎn)潔,也不容易出錯(cuò)。
3. 驗(yàn)證機(jī)制
驗(yàn)證用的是FluentValidation,支持同步和異步驗(yàn)證。比如創(chuàng)建部門的時(shí)候,需要檢查部門名稱是否已存在,就可以用異步驗(yàn)證:
public class CreateDeptCommandValidator : AbstractValidator<CreateDeptCommand>
{
public CreateDeptCommandValidator(DeptQuery deptQuery)
{
RuleFor(d => d.Name).NotEmpty().WithMessage("部門名稱不能為空");
// 異步驗(yàn)證:檢查部門名稱是否已存在
RuleFor(d => d.Name)
.MustAsync(async (n, ct) => !await deptQuery.DoesDeptExist(n, ct))
.WithMessage(d => $"該部門已存在,Name={d.Name}");
}
}4. 異常處理
業(yè)務(wù)異常用KnownException來(lái)處理,框架會(huì)自動(dòng)轉(zhuǎn)換成合適的HTTP狀態(tài)碼。比如在聚合根里:
// 在聚合根中
public void SoftDelete()
{
if (IsDeleted)
{
throw new KnownException("部門已經(jīng)被刪除");
}
// ...
}
// 在命令處理器中
var dept = await deptRepository.GetAsync(request.DeptId, cancellationToken)
?? throw new KnownException($"未找到部門,DeptId = {request.DeptId}");這樣前端收到的錯(cuò)誤信息就很清晰,不需要再做額外的轉(zhuǎn)換。
測(cè)試策略
測(cè)試這塊,項(xiàng)目用的是xUnit,集成測(cè)試用了Aspire來(lái)自動(dòng)管理測(cè)試環(huán)境。這樣做的好處是不用手動(dòng)搭建測(cè)試數(shù)據(jù)庫(kù)、Redis這些基礎(chǔ)設(shè)施,Aspire會(huì)自動(dòng)啟動(dòng)和管理。
看一個(gè)部門創(chuàng)建接口的測(cè)試?yán)樱?/p>
[Collection(WebAppTestCollection.Name)]
public class DeptTests(WebAppFixture app) : AuthenticatedTestBase<WebAppFixture>(app)
{
[Fact]
public async Task CreateDept_WithValidData_ShouldSucceed()
{
// Arrange
var client = await GetAuthenticatedClientAsync();
var deptName = $"測(cè)試部門_{Guid.NewGuid():N}";
try
{
// Act
var request = new CreateDeptRequest(deptName, "測(cè)試備注", null, 1);
var (response, result) = await client.POSTAsync<
CreateDeptEndpoint,
CreateDeptRequest,
ResponseData<CreateDeptResponse>>(request);
// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.NotNull(result?.Data);
Assert.Equal(deptName, result.Data.Name);
}
finally
{
await CleanupTestDataAsync();
}
}
}這種測(cè)試方式很接近真實(shí)的場(chǎng)景,測(cè)試的是完整的HTTP請(qǐng)求流程,而且會(huì)自動(dòng)清理測(cè)試數(shù)據(jù),保證測(cè)試之間的獨(dú)立性。另外還支持身份認(rèn)證測(cè)試,可以模擬登錄用戶的各種操作。
前端架構(gòu)
前端用的是Vben Admin這個(gè)模板,這是一個(gè)基于Vue 3的管理后臺(tái)框架。技術(shù)棧也比較主流:Vue 3 Composition API、TypeScript、Vite、Ant Design Vue,狀態(tài)管理用Pinia,路由用Vue Router。
Vben Admin這個(gè)框架做得很完善,開箱即用的功能很多。比如權(quán)限控制,支持路由權(quán)限和按鈕權(quán)限,用起來(lái)很方便。還有國(guó)際化支持,可以多語(yǔ)言切換。主題和布局也可以定制,基本的管理后臺(tái)需求都能滿足。
最重要的是類型安全,前后端都用了TypeScript,接口定義好之后,類型檢查能幫你發(fā)現(xiàn)很多問(wèn)題。
開發(fā)規(guī)范
為了讓代碼質(zhì)量更統(tǒng)一,項(xiàng)目里制定了一些開發(fā)規(guī)范。比如文件的組織方式:
- 聚合根放在
Domain/AggregatesModel/{AggregateName}Aggregate/ - 領(lǐng)域事件放在
Domain/DomainEvents/ - 倉(cāng)儲(chǔ)放在
Infrastructure/Repositories/ - 命令放在
Web/Application/Commands/{Module}Commands/ - 查詢放在
Web/Application/Queries/ - 端點(diǎn)放在
Web/Endpoints/{Module}Endpoints/
還有一些強(qiáng)制性的要求,比如所有聚合根都用強(qiáng)類型ID,而且不手動(dòng)賦值ID,依賴EF的值生成器。所有命令都要有對(duì)應(yīng)的驗(yàn)證器。領(lǐng)域事件要在聚合發(fā)生改變時(shí)發(fā)布。命令處理器不能調(diào)用SaveChanges,框架會(huì)自動(dòng)處理。倉(cāng)儲(chǔ)必須用異步方法。業(yè)務(wù)異常用KnownException處理。
另外,項(xiàng)目還提供了很多代碼片段,可以快速生成常用代碼。比如ncpcmd可以生成命令及其驗(yàn)證器和處理器,ncpar可以生成聚合根,ncprepo可以生成倉(cāng)儲(chǔ)接口和實(shí)現(xiàn),epp可以生成FastEndpoint的完整實(shí)現(xiàn)。這樣開發(fā)效率會(huì)高不少。
云原生支持
項(xiàng)目集成了.NET Aspire,這個(gè)功能真的很方便。啟動(dòng)開發(fā)環(huán)境只需要運(yùn)行AppHost項(xiàng)目,Aspire會(huì)自動(dòng)管理所有依賴服務(wù),不需要手動(dòng)啟動(dòng)數(shù)據(jù)庫(kù)、Redis、RabbitMQ這些。
# 僅需確保Docker環(huán)境運(yùn)行 docker version # 直接運(yùn)行AppHost項(xiàng)目,Aspire會(huì)自動(dòng)管理所有依賴服務(wù) cd src/Ncp.Admin.AppHost dotnet run
Aspire會(huì)自動(dòng)啟動(dòng)和管理數(shù)據(jù)庫(kù)容器(MySQL、PostgreSQL等)、消息隊(duì)列容器(RabbitMQ等)、Redis容器,還會(huì)提供統(tǒng)一的Aspire Dashboard界面,可以查看所有服務(wù)的狀態(tài)。服務(wù)之間的連接字符串也會(huì)自動(dòng)配置,省了很多麻煩。
代碼分析可視化
框架還提供了代碼流分析和可視化功能,這個(gè)對(duì)理解架構(gòu)很有幫助。可以通過(guò)命令行工具生成HTML文件:
# 安裝全局工具 dotnet tool install -g NetCorePal.Extensions.CodeAnalysis.Tools # 生成可視化文件 cd src/Ncp.Admin.Web netcorepal-codeanalysis generate --output architecture.html
BASH 復(fù)制 全屏
支持生成架構(gòu)流程圖、命令鏈路圖、事件流程圖、類圖等,可以直觀地看到代碼之間的關(guān)系和數(shù)據(jù)流向。
總結(jié)
這個(gè)項(xiàng)目算是一個(gè)DDD架構(gòu)的實(shí)踐案例,展示了如何在.NET 10生態(tài)中應(yīng)用DDD、CQRS、事件驅(qū)動(dòng)這些架構(gòu)思想。整體架構(gòu)清晰,職責(zé)分明,代碼組織得也比較規(guī)范。
技術(shù)棧上,后端用.NET 10 + EF Core + FastEndpoints + MediatR,前端用Vue 3 + TypeScript + Vite,都是目前比較主流的技術(shù)。開發(fā)體驗(yàn)上,有代碼片段、自動(dòng)化工具,還有完善的開發(fā)規(guī)范,開發(fā)效率還可以。
可維護(hù)性這塊,代碼分層清晰,測(cè)試支持也比較完善,還有代碼可視化工具,方便新人理解架構(gòu)。云原生支持也很到位,Aspire讓基礎(chǔ)設(shè)施管理變得簡(jiǎn)單。
如果你也在做類似的管理系統(tǒng),或者想了解DDD在實(shí)際項(xiàng)目中的應(yīng)用,可以看看這個(gè)項(xiàng)目的代碼,應(yīng)該能有一些參考價(jià)值。項(xiàng)目地址在https://github.com/zhouda1fu/Ncp.Admin,歡迎交流討論。
參考資料
最后附上一些相關(guān)的參考資料,有興趣的朋友可以深入了解一下:
- NetCorePal Cloud Framework - 項(xiàng)目使用的基礎(chǔ)框架
- FastEndpoints - 輕量級(jí)API框架
- Vben Admin - 前端管理后臺(tái)模板
- .NET Aspire - 云原生應(yīng)用開發(fā)平臺(tái)
項(xiàng)目源碼地址:https://github.com/zhouda1fu/Ncp.Admin
到此這篇關(guān)于基于NetCorePal Cloud Framework的DDD架構(gòu)管理系統(tǒng)實(shí)踐指南的文章就介紹到這了,更多相關(guān)NetCorePal Cloud Framework內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C#利用ASP.NET?Core開發(fā)學(xué)生管理系統(tǒng)詳解
- ASP.NET MVC5+EF6+EasyUI后臺(tái)管理系統(tǒng) 微信公眾平臺(tái)開發(fā)之消息管理
- ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后臺(tái)管理系統(tǒng)之前端頁(yè)面框架構(gòu)建源碼分享
- ASP.NET實(shí)現(xiàn)學(xué)生管理系統(tǒng)
- ASP.NET餐飲管理系統(tǒng)制作代碼分享
- ASP.NET網(wǎng)站管理系統(tǒng)退出 清除瀏覽器緩存,Session的代碼
- Asp.Net權(quán)限管理系統(tǒng) 專用代碼生成工具(DDBuildTools) 1.1.0 下載
相關(guān)文章
記一次EFCore類型轉(zhuǎn)換錯(cuò)誤及解決方案
這篇文章主要介紹了記一次EFCore類型轉(zhuǎn)換錯(cuò)誤及解決方案,幫助大家更好的理解和學(xué)習(xí)使用asp.net core,感興趣的朋友可以了解下2021-03-03
ASP.NET Core實(shí)現(xiàn)AES-GCM加密算法
這篇文章介紹了ASP.NET Core實(shí)現(xiàn)AES-GCM加密的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
ASP.NET MVC實(shí)現(xiàn)依賴注入的完整過(guò)程
這篇文章主要給大家介紹了關(guān)于ASP.NET MVC實(shí)現(xiàn)依賴注入的完整過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用ASP.NET MVC具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
Redis數(shù)據(jù)庫(kù)基礎(chǔ)與ASP.NET?Core緩存實(shí)現(xiàn)
這篇文章介紹了Redis數(shù)據(jù)庫(kù)基礎(chǔ)與ASP.NET?Core緩存實(shí)現(xiàn)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例 (System.NullReferenceException)
System.NullReferenceException:未將對(duì)象引用設(shè)置到對(duì)象的實(shí)例,這是一個(gè)新鳥,中鳥,老鳥都避不開的錯(cuò)誤2012-03-03
.net?core?api接口JWT方式認(rèn)證Token
本文詳細(xì)講解了.net?core?api接口JWT方式認(rèn)證Token,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-12-12

