ASP.NET Core 轻量级开源项目(遵循CleanArchitecture原则)
前言
这是一个基于最新的ASP.NET Core 5.0 创建Razor Page应用程序解决方案模板。遵循Clean Architecture的原则,以最求简洁的代码风格和实现快速开发小型的Web业务系统的目标,并且从没停止过更新。
该项目从最早的ASP.NET Web Form,ASP.NET MVC 5 到 ASP.NET Core 3.1再到现在最新的ASP.NET Core 5.0 Razor Page,从简单三层结构到N层结构再到现在流行的CQRS模式,一遍一遍的再重构,在这过程中体会到系统架构的重要性和在优秀的框架下开发系统是一件多么愉快的事情。
做这样的项目纯粹是为了兴趣和能和很多Github上优秀的程序员一起交流和学习。
介绍
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模式极简的代码风格
什么是CQRS:https://zhuanlan.zhihu.com/p/115685384
实现了基本的CRUD功能
实现了基本的认证和授权功能
支持多语言切换
项目结构
项目结构参考
jasontaylordev:https://github.com/jasontaylordev
CleanArchitecture:https://github.com/jasontaylordev/CleanArchitecture
基本功能预览
新增 修改 删除 查询 导入Excel 下载模板 导出Excel
用户管理
新增 修改 删除 查询 导入Excel 下载模板 导出Excel 重置密码 角色管理
角色管理
新增 修改 删除 查询 导入Excel 下载模板 导出Excel 授权管理
如何开始
在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中实现具体的功能请遵循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>
}
我的项目成果
网站 | 账号/密码 | 截图 |
---|---|---|
http://tms.i247365.net/TMS 运输管理系统 | demo@email.com/123456 | |
http://manage.i247365.net/ 委托业务内控管理系统 | demo/123456 | |
http://check.i247365.net/ 人脸考勤管理系统 | demo/123456 | |
http://supplier.i247365.net/ 供应商询价系统 | demo/123456 | |
http://iot.i247365.net/智能水务综合管理平台 | demo/123456 | |
http://book.i247365.net/小型图书管理工具 | demo/123456 |
最后
keep coding, enjoy coding. 如果你喜欢这个项目,请记得在Github上点赞,谢谢:https://github.com/neozhu/RazorPageCleanArchitecture
转自:阿新
链接:cnblogs.com/neozhu/p/15149673.html