10

一个遵循CleanArchitecture原则的Asp.net core轻量级开源项目

 3 years ago
source link: https://www.cnblogs.com/neozhu/p/15149673.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

这是一个基于最新的ASP.net core 5.0创建Razor Page应用程序解决方案模板。遵循Clean Architecture的原则,以最求简洁的代码风格和实现快速开发小型的web业务系统的目标,并且从没停止过更新。该项目从最早的asp.net web form,asp.net mvc5 到 asp.net core 3.1再到现在最新的asp.net core 5.0 Razor Page,从简单三层结构到N层结构再到现在流行的CQRS模式,一遍一遍的再重构,在这过程中体会到系统架构的重要性和在优秀的框架下开发系统是一件多么愉快的事情。做这样的项目纯粹是为了兴趣和能和很多Github上优秀的程序员一起交流和学习。
image.png

Technologies

image.png

基本功能预览

image.png

  • 导入Excel
  • 导出Excel

image.png

  • 导入Excel
  • 导出Excel

image.png

  • 导入Excel
  • 导出Excel
  1. 在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();
    }
  1. 在Application project中实现具体的功能请遵循CQRS模式
    image.png
  • Command
    • AddEdit
    • Delete
    • Import
  • Eventhandlers
  • Queries
    • Export
    • PaginationQuery
  1. 在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>
}

我的项目成果

keep coding, enjoy coding.
如果你喜欢这个项目,请记得在Github上点赞,谢谢
https://github.com/neozhu/RazorPageCleanArchitecture


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK