ASP.NET Core 輕量級開源項目(遵循CleanArchitecture原則)
前言
這是一個基于最新的ASP.NET Core 5.0 創(chuàng)建Razor Page應(yīng)用程序解決方案模板。遵循Clean Architecture的原則,以最求簡潔的代碼風(fēng)格和實現(xiàn)快速開發(fā)小型的Web業(yè)務(wù)系統(tǒng)的目標(biāo),并且從沒停止過更新。
該項目從最早的ASP.NET Web Form,ASP.NET MVC 5 到 ASP.NET Core 3.1再到現(xiàn)在最新的ASP.NET Core 5.0 Razor Page,從簡單三層結(jié)構(gòu)到N層結(jié)構(gòu)再到現(xiàn)在流行的CQRS模式,一遍一遍的再重構(gòu),在這過程中體會到系統(tǒng)架構(gòu)的重要性和在優(yōu)秀的框架下開發(fā)系統(tǒng)是一件多么愉快的事情。
做這樣的項目純粹是為了興趣和能和很多Github上優(yōu)秀的程序員一起交流和學(xué)習(xí)。

介紹
1、Github https://github.com/neozhu/RazorPageCleanArchitecture
2、Demo http://razor.i247365.net/
3、For Asp.net Core MVC https://github.com/neozhu/smartadmin.core.urf
Technologies
ASP.NET Core 5:https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-in-net-5/ Entity Framework Core 5:https://docs.microsoft.com/en-us/ef/core/ SmartAdmin - Responsive WebApp:https://wrapbootstrap.com/theme/smartadmin-responsive-webapp-WB0573SK0/ Razor Pages:https://docs.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-5.0&tabs=visual-studio Jquery EasyUI:https://www.jeasyui.com/ MediatR:https://github.com/jbogard/MediatR AutoMapper:https://automapper.org/ FluentValidation:https://fluentvalidation.net/ NUnit:https://nunit.org/ FluentAssertions:https://fluentassertions.com/ Moq:https://github.com/moq Respawn:https://github.com/jbogard/Respawn Docker:https://www.docker.com/
特點
遵循Clean Architecture原則
什么是Clean Architecture:https://www.youtube.com/watch?v=dK4Yb6-LxAk
非常漂亮的用戶界面
SmartAdmin - Responsive WebApp:https://wrapbootstrap.com/theme/smartadmin-responsive-webapp-WB0573SK0/
遵循CQRS模式極簡的代碼風(fēng)格
什么是CQRS:https://zhuanlan.zhihu.com/p/115685384
實現(xiàn)了基本的CRUD功能
實現(xiàn)了基本的認(rèn)證和授權(quán)功能
支持多語言切換
項目結(jié)構(gòu)

項目結(jié)構(gòu)參考
jasontaylordev:https://github.com/jasontaylordev
CleanArchitecture:https://github.com/jasontaylordev/CleanArchitecture
基本功能預(yù)覽

新增 修改 刪除 查詢 導(dǎo)入Excel 下載模板 導(dǎo)出Excel
用戶管理

新增 修改 刪除 查詢 導(dǎo)入Excel 下載模板 導(dǎo)出Excel 重置密碼 角色管理
角色管理

新增 修改 刪除 查詢 導(dǎo)入Excel 下載模板 導(dǎo)出Excel 授權(quán)管理
如何開始
在Domain project中新增一個Entity,比如Customer客戶信息
public partial class Customer : AuditableEntity, IHasDomainEvent
{
public int Id { get; set; }
public string Name { get; set; }
public string NameOfEnglish { get; set; }
public string GroupName { get; set; }
public PartnerType PartnerType { get; set; }
public string Region { get; set; }
public string Sales { get; set; }
public string RegionSalesDirector { get; set; }
public string Address { get; set; }
public string AddressOfEnglish { get; set; }
public string Contract { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Fax { get; set; }
public string Comments { get; set; }
public List<DomainEvent> DomainEvents { get; set; } = new();
}
在Application project中實現(xiàn)具體的功能請遵循CQRS模式
Command AddEdit Delete Import DTOs Eventhandlers Queries Export PaginationQuery
在SmartAdmin.WebUI中添加UI頁面
@page
@using CleanArchitecture.Razor.Domain.Enums
@using CleanArchitecture.Razor.Infrastructure.Constants.Permission
@model SmartAdmin.WebUI.Pages.Customers.IndexModel
@inject Microsoft.Extensions.Localization.IStringLocalizer<IndexModel> _localizer
@inject Microsoft.AspNetCore.Authorization.IAuthorizationService _authorizationService
@{
ViewData["Title"] = _localizer["Customers"].Value;
ViewData["PageName"] = "customers_index";
ViewData["Category1"] = _localizer["Customers"].Value;
ViewData["Heading"] = _localizer["Customers"].Value;
ViewData["PageDescription"] = _localizer["See all available options"].Value;
ViewData["PreemptiveClass"] = "Default";
var _canCreate = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Create);
var _canEdit = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Edit);
var _canDelete = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Delete);
var _canSearch = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Search);
var _canImport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Import);
var _canExport = await _authorizationService.AuthorizeAsync(User, null, Permissions.Customers.Export);
}
@section HeadBlock {
<link rel="stylesheet" media="screen, print" href="~/css/formplugins/bootstrap-daterangepicker/bootstrap-daterangepicker.css">
<link rel="stylesheet" media="screen, print" href="~/css/fa-solid.css">
<link rel="stylesheet" media="screen, print" href="~/css/theme-demo.css">
<link rel="stylesheet" media="screen,print" href="~/lib/easyui/themes/insdep/easyui.css">
<style>
.customer_dg_datagrid-cell-c1-_action {
overflow: visible !important
}
</style>
}
<div id="js-page-content-demopanels" class="card mb-g">
<div class="card-header bg-white d-flex align-items-center">
<h4 class="m-0">
@_localizer["Customers"]
<small>@_localizer["See all available options"]</small>
</h4>
<div class="ml-auto">
@if (_canCreate.Succeeded)
{
<button class="btn btn-sm btn-outline-primary " id="addbutton">
<span class="@(Settings.Theme.IconPrefix) fa-plus mr-1"></span>
@_localizer["Add"]
</button>
}
@if (_canDelete.Succeeded)
{
<button class="btn btn-sm btn-outline-danger" disabled id="deletebutton">
<span class="@(Settings.Theme.IconPrefix) fa-trash-alt mr-1"></span>
@_localizer["Delete"]
</button>
}
@if (_canSearch.Succeeded)
{
<button class="btn btn-sm btn-outline-primary " id="searchbutton">
<span class="@(Settings.Theme.IconPrefix) fa-search mr-1"></span>
@_localizer["Search"]
</button>
}
@if (_canImport.Succeeded)
{
<div class="btn-group" role="group">
<button id="importbutton" type="button" class="btn btn-sm btn-outline-primary waves-effect waves-themed">
<span class="@(Settings.Theme.IconPrefix) fa-upload mr-1"></span> @_localizer["Import Excel"]
</button>
<button type="button" class="btn btn-sm btn-outline-primary dropdown-toggle dropdown-toggle-split waves-effect waves-themed" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Toggle Dropdown</span>
</button>
<div class="dropdown-menu" aria-labelledby="importbutton">
<button id="gettemplatebutton" class="dropdown-item">@_localizer["Download Template"]</button>
</div>
</div>
}
@if (_canExport.Succeeded)
{
<button class="btn btn-sm btn-outline-primary " id="exportbutton">
<span class="@(Settings.Theme.IconPrefix) fa-download mr-1"></span>
@_localizer["Export Excel"]
</button>
}
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-12">
<table id="customer_dg">
</table>
</div>
</div>
</div>
</div>
<div class="modal fade" id="customer_modal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true"><i class="@(Settings.Theme.IconPrefix) fa-times"></i></span>
</button>
</div>
<form id="customer_form" class="needs-validation" novalidate="novalidate">
...
</form>
</div>
</div>
</div>
@await Component.InvokeAsync("ImportExcel", new { importUri = Url.Page("/Customers/Index") + "?handler=Import",
getTemplateUri = @Url.Page("/Customers/Index") + "?handler=CreateTemplate",
onImportedSucceeded = "reload()" })
@section ScriptsBlock {
<partial name="_ValidationScriptsPartial" />
<script type="text/javascript" src="~/lib/easyui/jquery.easyui.min.js" asp-append-version="true"></script>
<script type="text/javascript" src="~/lib/easyui/jquery.easyui.component.js" asp-append-version="true"></script>
<script type="text/javascript" src="~/lib/easyui/plugins/datagrid-filter.js" asp-append-version="true"></script>
<script>jQuery.fn.tooltip = bootstrapTooltip;</script>
<script src="~/lib/axios/dist/axios.js"></script>
<script src="~/lib/jquery-form/jquery.jsonToForm.js"></script>
<script type="text/javascript">
$('#searchbutton').click(function () {
reload();
});
$('#addbutton').click(function () {
popupmodal(null);
});
$('#deletebutton').click(function () {
onDeleteChecked();
});
$('#exportbutton').click(function () {
onExport();
});
$('#importbutton').click(function () {
showImportModal();
});
$('#gettemplatebutton').click(function () {
onGetTemplate();
});
$('#customer_form :submit').click(function (e) {
...
event.preventDefault();
event.stopPropagation();
})
var $dg={};
var initdatagrid = () => {
$dg = $('#customer_dg').datagrid({
...
}
var reload = () => {
$dg.datagrid('load', '@Url.Page("/Customers/Index")?handler=Data');
}
$(() => {
initdatagrid();
})
var popupmodal = (customer) => {
...
}
var onEdit = (index) => {
var customer = $dg.datagrid('getRows')[index];
popupmodal(customer);
}
var onDelete = (id) => {
...
}
var onDeleteChecked = () => {
...
}
var onExport = () => {
...
}
</script>
}
我的項目成果
| 網(wǎng)站 | 賬號/密碼 | 截圖 |
|---|---|---|
| http://tms.i247365.net/TMS 運輸管理系統(tǒng) | [email protected]/123456 | ![]() |
| http://manage.i247365.net/ 委托業(yè)務(wù)內(nèi)控管理系統(tǒng) | demo/123456 | ![]() |
| http://check.i247365.net/ 人臉考勤管理系統(tǒng) | demo/123456 | ![]() |
| http://supplier.i247365.net/ 供應(yīng)商詢價系統(tǒng) | demo/123456 | ![]() |
| http://iot.i247365.net/智能水務(wù)綜合管理平臺 | demo/123456 | ![]() |
| http://book.i247365.net/小型圖書管理工具 | demo/123456 | ![]() |
最后
keep coding, enjoy coding. 如果你喜歡這個項目,請記得在Github上點贊,謝謝:https://github.com/neozhu/RazorPageCleanArchitecture
轉(zhuǎn)自:阿新
鏈接:cnblogs.com/neozhu/p/15149673.html






