# CSharp-Language-Specification **Repository Path**: csit99/CSharp-Language-Specification ## Basic Information - **Project Name**: CSharp-Language-Specification - **Description**: C# 编码规范 - **Primary Language**: C# - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3 - **Created**: 2022-12-12 - **Last Updated**: 2022-12-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # C# 编码规范 [1 前言](#user-content-1-%E5%89%8D%E8%A8%80) [2 代码风格](#user-content-2-%E4%BB%A3%E7%A0%81%E9%A3%8E%E6%A0%BC)   [2.1 文件](#user-content-21-%E6%96%87%E4%BB%B6)   [2.2 结构](#user-content-22-%E7%BB%93%E6%9E%84)     [2.2.1 缩进](#user-content-221-%E7%BC%A9%E8%BF%9B)     [2.2.2 空格](#user-content-222-%E7%A9%BA%E6%A0%BC)     [2.2.3 换行](#user-content-223-%E6%8D%A2%E8%A1%8C)     [2.2.4 语句](#user-content-224-%E8%AF%AD%E5%8F%A5)   [2.3 命名](#user-content-23-%E5%91%BD%E5%90%8D)   [2.4 注释](#user-content-24-%E6%B3%A8%E9%87%8A)     [2.4.1 单行注释](#user-content-241-%E5%8D%95%E8%A1%8C%E6%B3%A8%E9%87%8A)     [2.4.2 多行注释](#user-content-242-%E5%A4%9A%E8%A1%8C%E6%B3%A8%E9%87%8A)     [2.4.3 摘要注释](#user-content-243-%E6%91%98%E8%A6%81%E6%B3%A8%E9%87%8A)     [2.4.4 细节注释](#user-content-244-%E7%BB%86%E8%8A%82%E6%B3%A8%E9%87%8A) [3 项目结构](#user-content-3-%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84)     [3.1 领域类](#user-content-31-%E9%A2%86%E5%9F%9F%E7%B1%BB)     [3.2 基础设施类](#user-content-32-%E5%9F%BA%E7%A1%80%E8%AE%BE%E6%96%BD%E7%B1%BB)     [3.3 应用服务类](#user-content-33-%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E7%B1%BB)     [3.4 控制器](#user-content-34-%E6%8E%A7%E5%88%B6%E5%99%A8)     [3.5 AspNet-MVC宿主服务](#user-content-35-AspNet-MVC%E5%AE%BF%E4%B8%BB%E6%9C%8D%E5%8A%A1) ## 1 前言 本文档的目标是使 C# 代码风格保持一致,容易被理解和被维护,应尽量遵循本文档的约定。 ## 2 代码风格 ### 2.1 文件 ##### [建议] 文件使用无 `BOM` 的 `UTF-8` 编码。 解释: UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。 ##### [建议] 在文件结尾处,保留一个空行。 ### 2.2 结构 #### 2.2.1 缩进 ##### [强制] 使用 `tab` 做为一个缩进层级,不允许使用空格。 ##### [强制] `switch` 下的 `case` 和 `default` 必须增加一个缩进层级。 示例: ```C# // good switch (strProcCode) { //TODO: 业务逻辑(申请、同意、拒绝、运行中撤销、同意后撤销) case "PROC0001": //请假 tuple = AskForLeaveLogic.Approval(ref input); break; case "PROC0101": //申请寝室长 tuple = SuSheLogic.DormitoryLeader(ref input); break; case "PROC0102": //申请留校 tuple = SuSheLogic.StayDormitory(ref input); break; } // bad switch (strProcCode) { //TODO: 业务逻辑(申请、同意、拒绝、运行中撤销、同意后撤销) case "PROC0001": //请假 tuple = AskForLeaveLogic.Approval(ref input); break; case "PROC0101": //申请寝室长 tuple = SuSheLogic.DormitoryLeader(ref input); break; case "PROC0102": //申请留校 tuple = SuSheLogic.StayDormitory(ref input); break; } ``` #### 2.2.2 空格 ##### [强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。 示例: ```C# var a = !arr.Count; a++; a = b + c; ``` ##### [强制] `if / for / while / switch / try / catch` 关键字后,必须有一个空格。 示例: ```C# // good if (condition) { } while (condition) { } // bad if(condition) { } while(condition) { } ``` ##### [强制] 方法声明、方法调用中,方法名和 `(` 之间不允许有空格。 示例: ```C# // good public static APOutput CreateProcessInstance(APInput input) { } var output = ProcessLogic.CreateProcessInstance(input); // bad public static APOutput CreateProcessInstance (APInput input) { } var output = ProcessLogic.CreateProcessInstance (input); ``` ##### [强制] `,` 和 `;` 前不允许有空格。如果不位于行尾,`,` 和 `;` 后必须跟一个空格。 示例: ```C# // good CallFunc(a, b); // bad CallFunc(a , b) ; ``` ##### [强制] 在方法调用、方法声明、括号表达式、属性访问、`if / for / while / switch / catch` 等语句中,`()` 和 `[]` 内紧贴括号部分不允许有空格。 示例: ```C# // good CallFunc(param1, param2, param3); Save(list[indexes[i]]); needIncream && (variable += increament); if (num > list.Count) { } while (len--) { } // bad CallFunc( param1, param2, param3 ); Save( list[ indexes[ i ] ] ); needIncreament && ( variable += increament ); if ( num > list.Count ) { } while ( len-- ) { } ``` ##### [强制] 行尾不得有多余的空格。 #### 2.2.3 换行 ##### [强制] 每个独立语句结束后必须换行。 ##### [强制] 用作代码块起始的左花括号 `{` 前必须换行。 示例: ```C# // good if (condition) { } while (condition) { } public static APOutput CreateProcessInstance(APInput input) { } // bad if (condition) { } while (condition) { } public static APOutput CreateProcessInstance(APInput input) { } ``` ##### [强制] 每行不得超过 `120` 个字符。 解释: 超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。 ##### [强制] 运算符处换行时,运算符必须在新行的行首。 示例: ```C# // good if (user.IsAuthenticated() && user.IsInRole('admin') && user.HasAuthority('add-admin') || user.HasAuthority('delete-admin')) { // Code } var result = number1 + number2 + number3 + number4 + number5; // bad if (user.IsAuthenticated() && user.IsInRole('admin') && user.HasAuthority('add-admin') || user.HasAuthority('delete-admin')) { // Code } var result = number1 + number2 + number3 + number4 + number5; ``` ##### [建议] 不同行为或逻辑的语句集,使用空行隔开,更易阅读。 示例: ```C# // 仅为按逻辑换行的示例,不代表SetValue的最优实现 public static void SetValue(JObject obj, string key, JToken value) { if (obj == null) { return; } obj[key] = value; } ``` ##### [建议] 对于 `if...else...`、`try...catch...finally` 等语句,推荐使用在 `}` 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。 示例: ```C# if (condition) { // some statements; } else { // some statements; } try { // some statements; } catch (ex) { // some statements; } ``` #### 2.2.4 语句 ##### [强制] 在 `if / else / for / do / while` 语句中,即使只有一行,也不得省略块 `{...}`。 示例: ```C# // good if (condition) { CallFunc(); } // bad if (condition) CallFunc(); if (condition) CallFunc(); ``` ### 2.3 命名 ##### [强制] `private` 修饰的 `变量` 使用 `camel命名法`,并加 `下划线`。 示例: ```C# private int _count = 0; ``` ##### [强制] `private static` 修饰的 `变量` 使用 `camel命名法`,并加 `下划线`。 示例: ```C# private static int _count = 0; ``` ##### [强制] `private readonly` 修饰的 `变量` 使用 `camel命名法`,并加 `下划线`。 示例: ```C# private readonly int _count = 0; ``` ##### [强制] `private static readonly` 修饰的 `变量` 使用 `Pascal命名法`。 示例: ```C# private static readonly int Count = 0; ``` ##### [强制] 只要有 `public` 修饰的 `变量` 就使用 `Pascal命名法`。 示例: ```C# public int Count = 0; ``` ##### [强制] `局部变量` 使用 `camel命名法`。 示例: ```C# var count = 0; ``` ##### [强制] `类中的常量` 使用 `Pascal命名法`。 示例: ```C# private const int Count = 0; ``` ##### [强制] `全局常量` 使用 `全部字母大写,单词间下划线分隔` 的命名方式。 示例: ```C# public const SYNC_COUNT = 0; ``` ##### [强制] `方法` 使用 `Pascal命名法`。 示例: ```C# public static void Sync() { } ``` ##### [强制] 方法的 `参数` 使用 `camel命名法`。 示例: ```C# public static void Sync(int count) { } ``` ##### [强制] `类` 使用 `Pascal命名法`。 示例: ```C# public class Logic { } ``` ##### [强制] `枚举变量` 使用 `Pascal命名法`,`枚举的属性` 使用 `Pascal命名法` 或 `全部字母大写,单词间下划线分隔` 的命名方式。 示例: ```C# /// /// 审批状态 /// public enum APProcessStatus { /// /// 新创建 /// NEW = 0, /// /// 运行中 /// RUNNING = 1, /// /// 取消 /// CANCELED = 2, /// /// 被终止 /// TERMINATED = 3, /// /// 完成 /// COMPLETED = 4 } ``` ##### [强制] `命名空间` 使用 `Pascal命名法`。 示例: ```C# namespace WebYKT.Model.Enums { } ``` ##### [强制] 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。 示例: ```C# public static string XMLParser(string strXML) { } public static void InsertXML(string strXML) { } var httpRequest = new HTTPRequest(); ``` ##### [强制] `类名` 使用 `名词`。 示例: ```C# public class Logic { } ``` ##### [建议] `方法名` 使用 `动宾短语`。 示例: ```C# public static void InsertXML(string strXML) { } ``` ##### [建议] `async` 修饰的 `方法名` 加后缀 `Async` 。 示例: ```C# public static async Task InsertXMLAsync(string strXML) { } ``` ##### [建议] `bool` 类型的变量使用 `is` 或 `has` 开头。 示例: ```C# var isReady = false; var hasMoreCommands = false; ``` ##### [建议] `接口` 使用 字母`I`开头。 示例: ```C# /// /// 用户服务接口 /// public interface IUserAppService ``` #### [强制] WinForm程序开发,组件名称缩写约定
组件类型 缩写 例子
Label Lbl lblNote
TextBox Txt txtName
Button Btn btnOK
ImageButton Ib ibOK
LinkButton Lb lbJump
HyperLink Hl hlJump
DropDownList Ddl ddlList
CheckBox Cb cbChoice
CheckBoxList Cbl cblGroup
RadioButton Rb rbChoice
RadioButtonList Rbl rblGroup
Image Img imgBeauty
Panel Pnl pnlTree
TreeView Tv tvUnit
WebComTable Wct wctBasic
ImageDateTimeInput Dti dtiStart
ComboBox Cb cbList
MyImageButton Mib mibOK
WebComm.TreeView Tv tvUnit
PageBar Pb pbMaster
### 2.4 注释 #### 2.4.1 单行注释 ##### [强制] 必须独占一行。`//` 后跟一个空格,缩进与下一行被注释说明的代码一致。 #### 2.4.2 多行注释 ##### [建议] 避免使用 `/*...*/` 这样的多行注释。有多行注释内容时,使用多个单行注释。 #### 2.4.3 摘要注释 ##### [强制] 为了便于代码阅读,以下内容必须包含以 `` 形式的摘要注释中。 解释: 1. 文件注释 ```C# /******************************************************************************** ** Class Name: SysProjectRequestDto ** Description: 项目信息数据传输请求对象 ** Author: Charles ** Create Time: 2022年12月12日 20:22:56 ** Last Modified By: ** Last Modified Time: ** Copy Right (R): 辽宁中软信息技术有限公司(csit99.com) 2022 *********************************************************************************/ ``` 2. 类注释 ``` C# /// /// 项目信息数据传输请求对象 /// ``` 3. 方法或方法 ```C# /// /// 添加项目信息 /// /// 项目信息数据传输请求对象 /// [NeedAuthorize] [HttpPost] [BusinessActionLog(Name = "添加项目信息")] [Route("Create")] public async Task Create(SysProjectRequestDto request) { return await _sysProjectService.CreateAsync(request); } ``` 4. 类属性 ``` C# /// /// 项目Id /// [Description("项目Id")] public long? ProjectId { get; set; } ``` 6. 事件 ``` C# /// /// 我是一个事件 /// ``` 7. 全局变量 ``` C# /// /// 我是一个全局变量,不过通常在AspNet-Mvc项目中很少使用 /// ``` 8. 常量 ``` C# /// /// 我是一个常量 /// public const int OK = 0; ``` #### 2.4.4 细节注释 对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。 ##### [建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。 示例: ```C# public static string Request(string strUri) { // 这里对具体内部逻辑进行说明 // 说明太长需要换行 for (...) { .... } } ``` ##### [强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记: 解释: 1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。 2. #region #endregion: 注释其中间的代码段,或者折叠中间的代码块。注意#region和#endregion的上下换行。 ```C# #region 说明 // …… #endregion ``` #### 2.5 代码组织与风格 #### 2.5.1 Tab 要使一个Tab为4个空格长。 #### 2.5.2 缩进 要使一个代码块内的代码都统一缩进一个Tab长度。 #### 2.5.3 空行 建议适当的增加空行,来增加代码的可读性。 在在类,接口以及彼此之间要有两行空行: 在下列情况之间要有一行空行: 方法之间; 局部变量和它后边的语句之间; 方法内的功能逻辑部分之间; #### 2.5.4 函数长度 每个函数有效代码(不包括注释和空行)长度不要超过50行。 #### 2.5.5 “{”,“}” 开括号“{”要放在块的所有者的下一行,单起一行; 闭括号“}”要单独放在代码块的最后一行,单起一行。 #### 2.5.5 行宽 每行代码和注释不要超过70个字符或屏幕的宽度,如超过则应换行,换行后的代码应该缩进一个Tab。 #### 2.5.6 空格 括号和它里面的字符之间不要出现空格。括号应该和它前边的关键词留有空格,如:while (true) {}; 但是方法名和左括号之间不要有空格。 参数之间的逗号后要加一空格。如:method1(int i1, int i2) for语句里的表达式之间要加一空格。如:for (expr1; expr2; expr3) 二元操作符和操作数之间要用空格隔开。如:i + c; 强制类型转换时,在类型和变量之间要加一空格。如:(int) i ; ## 3 代码结构 ##### [强制] 我们参照 Domain Driven Design (领域驱动设计,DDD) 的模式设计软件架构 1. 解决方案目录下 `Application` 目录下存储的项目为 `应用服务`。 2. 解决方案目录下 `Controller` 目录下存储的项目为 `控制器`。 3. 解决方案目录下 `Domain` 目录下存储的项目为 `领域服务`。 4. 解决方案目录下 `Infrastructure` 目录下存储的项目为 `基础设施`。 5. 解决方案根目录下以 `Host` 结尾的项目为 `AspNet-MVC宿主服务`。 ### 3.1 领域类 ##### [强制] 在领域服务项目下使用如下约定定义目录名称 #### 3.1.1. 使用 `Convert` 目录存放自定义TinyMapper对象映射转换器。 #### 3.1.2. 使用 `Dto` 目录存储数据传输对象,此目录下分为`Request`和`Response`两个目录,分别存储 `请求` 对象和 `输出` 对象。 在 `Request` 目录下的请求对象,数据属性应为可空类型,使用 `?` 进行修饰, 如: ```C# /******************************************************************************** ** Class Name: SysProjectRequestDto ** Description: 项目信息数据传输请求对象 ** Author: Charles ** Create Time: 2022年12月12日 20:22:56 ** Last Modified By: ** Last Modified Time: ** Copy Right (R): 辽宁中软信息技术有限公司(csit99.com) 2022 *********************************************************************************/ using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace LNCS.User.Authority.System.Domain.Dto; /// /// 项目信息数据传输请求对象 /// public class SysProjectRequestDto { /// /// Id /// [Description("Id")] public long? Id { get; set; } /// /// 项目Id /// [Description("项目Id")] public long? ProjectId { get; set; } /// /// 项目父Id /// [Description("项目父Id")] public long? ParentId { get; set; } /// /// 等级 /// [Description("等级")] public byte? Level { get; set; } /// /// 项目编号 /// [Description("项目编号")] [MaxLength(200)] public string? ProjectNo { get; set; } /// /// 项目名称 /// [Description("项目名称")] [MaxLength(200)] public string? ProjectName { get; set; } /// /// 项目简称 /// [Description("项目简称")] [MaxLength(200)] public string? ShortName { get; set; } /// /// 项目描述 /// [Description("项目描述")] [MaxLength(200)] public string? ProjectDescription { get; set; } /// /// 项目建设单位 /// [Description("项目建设单位")] [MaxLength(200)] public string? ProjectUnit { get; set; } /// /// 实际开始时间 /// [Description("实际开始时间")] public DateTime? StartTime { get; set; } /// /// 实际结束时间 /// [Description("实际结束时间")] public DateTime? EndTime { get; set; } /// /// 计划开始时间 /// [Description("计划开始时间")] public DateTime? PlanStartTime { get; set; } /// /// 计划结束时间 /// [Description("计划结束时间")] public DateTime? PlanEndTime { get; set; } /// /// 项目计划投资 /// [Description("项目计划投资")] [MaxLength(500)] public string? ProjectInvest { get; set; } /// /// 排序字段 /// [Description("排序字段")] public ushort? Sorted { get; set; } /// /// 备注 /// [Description("备注")] [MaxLength(200)] public string? Remark { get; set; } /// /// 创建用户Id /// [Description("创建用户Id")] public long? CreateUserId { get; set; } /// /// 创建用户Id /// [Description("创建用户Id")] public long? UpdateUserId { get; set; } /// /// 创建时间 /// [Description("创建时间")] public DateTime? CreateTime { get; set; } /// /// 更新时间 /// [Description("更新时间")] public DateTime? UpdateTime { get; set; } /// /// 数据可用状态(1-启用,0-禁用) /// [Description("数据可用状态(1-启用,0-禁用)")] public sbyte? Status { get; set; } = 1; /// /// 逻辑删除状态(0-正常,1-已删除) /// [Description("逻辑删除状态(0-正常,1-已删除)")] public sbyte? IsDel { get; set; } = 0; } ``` 在 `Response` 目录下的输出对象,数据属性应为可空类型,使用 `?` 进行修饰,并且由于JavaScript解析Long型数据丢失精度, 故将long型数据以string型输出,如: ```C# /******************************************************************************** ** Class Name: SysProjectResponseDto ** Description: 项目信息数据传输输出对象 ** Author: Charles ** Create Time: 2022年12月12日 21:15:51 ** Last Modified By: ** Last Modified Time: ** Copy Right (R): 辽宁中软信息技术有限公司(csit99.com) 2022 *********************************************************************************/ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using LNCS.Framework.Application.Dto; namespace LNCS.User.Authority.System.Domain.Dto; /// /// 项目信息数据传输输出对象 /// public class SysProjectResponseDto:CBResult { /// /// 项目Id /// [Description("项目Id")] public string? ProjectId { get; set; } /// /// 项目父Id /// [Description("项目父Id")] public string? ParentId { get; set; } /// /// 父Id集合 /// [Description("父Id父Id集合")] public List ParentIdList { get; set; } = new(); /// /// 等级 /// [Description("等级")] public byte? Level { get; set; } /// /// 项目编号 /// [Description("项目编号")] [MaxLength(200)] public string? ProjectNo { get; set; } /// /// 项目名称 /// [Description("项目名称")] [MaxLength(200)] public string? ProjectName { get; set; } /// /// 项目简称 /// [Description("项目简称")] [MaxLength(200)] public string? ShortName { get; set; } /// /// 项目描述 /// [Description("项目描述")] [MaxLength(200)] public string? ProjectDescription { get; set; } /// /// 项目建设单位 /// [Description("项目建设单位")] [MaxLength(200)] public string? ProjectUnit { get; set; } /// /// 实际开始时间 /// [Description("实际开始时间")] public DateTime? StartTime { get; set; } /// /// 实际结束时间 /// [Description("实际结束时间")] public DateTime? EndTime { get; set; } /// /// 计划开始时间 /// [Description("计划开始时间")] public DateTime? PlanStartTime { get; set; } /// /// 计划结束时间 /// [Description("计划结束时间")] public DateTime? PlanEndTime { get; set; } /// /// 项目计划投资 /// [Description("项目计划投资")] [MaxLength(500)] public string? ProjectInvest { get; set; } /// /// 排序字段 /// [Description("排序字段")] public ushort? Sorted { get; set; } /// /// 备注 /// [Description("备注")] [MaxLength(200)] public string? Remark { get; set; } /// /// 创建用户Id /// [Description("创建用户Id")] public string? CreateUserId { get; set; } /// /// 创建用户Id /// [Description("创建用户Id")] public string? UpdateUserId { get; set; } /// /// 创建时间 /// [Description("创建时间")] public DateTime? CreateTime { get; set; } /// /// 更新时间 /// [Description("更新时间")] public DateTime? UpdateTime { get; set; } /// /// 数据可用状态(1-启用,0-禁用) /// [Description("数据可用状态(1-启用,0-禁用)")] public sbyte? Status { get; set; } = 1; /// /// 删除状态(0-正常,1-已删除) /// [Description("删除状态(0-正常,1-已删除)")] public sbyte? IsDel { get; set; } = 0; /// /// 子节点数据 /// public List Children { get; set; } = new(); } ``` #### 3.1.3. 使用 `Entities` 目录存放数据库表实体类。 解释:在项目开发时,可自行选择实体类继承的基类 1. `BizIdentityLiteEntity`: 为数据库实体基类, 只定义包含` Id` 主键字段。 2. `BizIdentityEntity`: 为数据库实体基类, 定义包含`Id`主键字段, `CreateTime`创建时间字段,`UpdateTime` 更新时间字段。 3. `BizIdentityExtendEntity`: 为数据库实体基类, 包含`BizIdentityEntity`类所有字段外,还包含`CreateUserId`创建用户Id字段和`UpdateUserId`更新用户Id字段。 4. `[Table('sys_project')]`: Table为EntityFramework注解,标明数据库中以括号内字符串命名数据库表。 5. `[Description("项目Id")]`: Description为字段增加描述信息。 6. `[MaxLength(200)]`:MaxLength为指定数据库varchar型字段长度,如不添加此注解,则默认为longtext类型,同时在数据表中应合理使用varchar型字段,如必要的varchar较多时,则应为其建立扩展映射表。 7. `[Column("ProjectInvest",TypeName="varchar(200)")]`:`Column` 注解使用场景是当数据库中已有字段与数据库定义字段名称不相同时,请使用此注解。后面 `TypeName` 为指定字段数据类型和长度为`varchar(200)`。 ```C# /******************************************************************************** ** Class Name: TbSysProjectEntity ** Description: 系统项目信息 ** Author: Charles ** Create Time: Thursday, 09 December 2021 10:39:12 ** Last Modified By: ** Last Modified Time: ** Copy Right (R): 辽宁中软信息技术有限公司(csit99.com) 2021 *********************************************************************************/ using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using LNCS.Framework.Domain.Entities; namespace LNCS.User.Authority.System.Domain.Entities; /// /// 系统项目信息 /// [Table("sys_project")] public class TbSysProjectEntity : BizIdentityExtendEntity { /// /// 项目Id /// [Description("项目Id")] public long ProjectId { get; set; } /// /// 项目父Id /// [Description("项目父Id")] public long ParentId { get; set; } /// /// 等级 /// [Description("等级")] public byte Level { get; set; } /// /// 项目编号 /// [Description("项目编号")] [MaxLength(200)] public string ProjectNo { get; set; } /// /// 项目名称 /// [Description("项目名称")] [MaxLength(200)] public string ProjectName { get; set; } /// /// 项目简称 /// [Description("项目简称")] [MaxLength(200)] public string ShortName { get; set; } /// /// 项目描述 /// [Description("项目描述")] [MaxLength(200)] public string ProjectDescription { get; set; } /// /// 项目建设单位 /// [Description("项目建设单位")] [MaxLength(200)] public string ProjectUnit { get; set; } /// /// 实际开始时间 /// [Description("实际开始时间")] public DateTime? StartTime { get; set; } /// /// 实际结束时间 /// [Description("实际结束时间")] public DateTime? EndTime { get; set; } /// /// 计划开始时间 /// [Description("计划开始时间")] public DateTime? PlanStartTime { get; set; } /// /// 计划结束时间 /// [Description("计划结束时间")] public DateTime? PlanEndTime { get; set; } /// /// 项目计划投资 /// [Description("项目计划投资")] [MaxLength(500)] public string? ProjectInvest { get; set; } /// /// 排序字段 /// [Description("排序字段")] public ushort Sorted { get; set; } /// /// 备注 /// [Description("备注")] [MaxLength(200)] public string? Remark { get; set; } } ``` #### 3.1.4. 使用 `Enum` 目录存放枚举类。 解释:在项目开发时,对于不经常变化的数值,尽量多使用枚举, 注意在数据库表中定义枚举字段时,应选择合适的数据类型; C#与MySql数据库字段对照表
C# 描述 取值范围 默认值 MySql 描述 取值范围 默认值
bool 布尔值 True或False False -- -- -- --
byte 8位无符号整数 0到255 0 TINYINT 小整数值 无符号(0,255) 在定义表实体时指定默认值,尽量将字段设置为非null
sbyte 8位有符号整数 -128到127 0 TINYINT 小整数值 有符号(-128,127) 在定义表实体时指定默认值,尽量将字段设置为非null
short 16位有符号整数类型 -32,768到32,767 0 SMALLINT 大整数值 (-32,768到32,767) 在定义表实体时指定默认值,尽量将字段设置为非null
ushort 16位无符号整数类型 0到65,535 0 SMALLINT 大整数值 (0到65,535) 在定义表实体时指定默认值,尽量将字段设置为非null
int 32位有符号整数类型 -2,147,483,648到2,147,483,647 0 MEDIUMINT或INT 大整数值 三字节的MEDIUMINT为(-8,388 608到8,388,607), 四字节的INT为(-2,147,483,648到2,147,483,647) 在定义表实体时指定默认值,尽量将字段设置为非null
uint 32位无符号整数类型 0到4,294,967,295 0 MEDIUMINT或INT 大整数值 三字节的MEDIUMINT为((0,16,777,215)), 四字节的INT为(0到4,294,967,295) 在定义表实体时指定默认值,尽量将字段设置为非null
long 64位有符号整数类型 -9,223,372,036,854,775,808到9,223,372,036,854,775,807 0 BIGINT 大整数值 四字节的BIGINT为((-9,223,372,036,854,775,808到9,223,372,036,854,775,807)) 在定义表实体时指定默认值,尽量将字段设置为非null
ulong 64位无符号整数类型 0到18,446,744,073,709,551,615 0 BIGINT 极大整数值 四字节的BIGINT为(0到18,446,744,073,709,551,615) 在定义表实体时指定默认值,尽量将字段设置为非null
#### 3.1.5. 使用 `Exceptions` 目录存放异常类,所有异常类均应继承自`BaseException`类。 解释:在项目开发时,按异常类型自定义异常类,将大大简化异常信息的处理,将同一类的异常信息进行封装,自定义错误提示信息和错误代码; #### 3.1.6. 使用 `Service` 目录存放领域服务类,所有领域服务均应继承自`DomainService`类, 在该类中实现对数据库表的基础操作。 ```C# /******************************************************************************** ** Class Name: TbSysUserDomainService ** Description: 系统用户领域服务 ** Author: Charles ** Create Time: Thursday, 09 December 2021 10:39:12 ** Last Modified By: ** Last Modified Time: ** Copy Right (R): 辽宁中软信息技术有限公司(csit99.com) 2021 *********************************************************************************/ using Kay.Boilerplate.Domain.Entities; using Kay.Framework.Domain.Repositories; using Kay.Framework.Domain.Services; using Kay.Framework.RegisterInterfaces; using System; using System.Collections.Generic; using System.Text; namespace LNCS.User.Authority.System.Domain.Service; /// /// 系统用户领域服务 /// public class TbSysUserDomainService: DomainService { private readonly IRepository _sysUserRepository; public TbUserDomainService( IServiceProvider serviceProvider, IRepository sysUserRepository) : base(serviceProvider) { _sysUserRepository = sysUserRepository; } public TbUserEntity Get(long id) { var entity = _sysUserRepository.GetById(id); return entity; } public bool Create(TbUserEntity entity) { _sysUserRepository.Add(entity); return UnitOfWork.SaveChanges() > 0; } } ``` #### 3.1.7. 使用 `Specifictions` 目录存放数据库EntityFramework查询表达式,所有领域服务的查询表达式均应继承自`BaseSpecification`类。 解释:在实际项目开发时,通过查询表达式类将大大简化EntityFramework查询时重复编写查询表达式,提升代码复用性。 ### 3.2 基础设置类 ##### [强制] 在基础设施项目下使用如下约定定义目录名称 #### 3.2.1. 使用 `mapping` 目录存放数据库EntityFramework索引配置类。根据不同的实体基类,需要从 `EntityExtendTypeConfiguration` , `EntityLiteTypeConfiguration` 或 `EntityTypeConfiguration` 继承。 如下代码: ```C# public class TbSysBidSectionEntityMap : EntityExtendTypeConfiguration { public override void Configure(EntityTypeBuilder builder) { builder.HasIndex(c => new { c.SectionId }); //普通索引 builder.HasIndex(c => new { c.SectionName }).IsUnique(); //唯一索引 builder.HasIndex(c => new { c.SectionNo }).IsUnique(); //唯一索引 } } ``` 配置如何使用DotNet EntityFramework 进行数据库迁移 ```C# 引入Nuget包 添加类 DesignTimeDBContextFactory.cs using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; namespace LNCS.User.Authority.System.Infrastructure.BoundedContext.EF; public class DesignTimeDBContextFactory :IDesignTimeDbContextFactory { public UASDBContext CreateDbContext(string[] args) { var configuration = BuildConfiguration(); var connectionString = configuration["DbConnectionString"]; //设置数据库连接 var builder = new DbContextOptionsBuilder() .UseMySql(connectionString,ServerVersion.AutoDetect(connectionString)); return new UASDBContext(builder.Options,configuration); } private static IConfigurationRoot BuildConfiguration() { var builder = new ConfigurationBuilder() .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../LNCS.User.Authority.System.Service.Host/")) .AddJsonFile("appsettings.json", optional: true); //设置配置文件路径 return builder.Build(); } } dotnet ef migrations add AddTable_WorkOrderSystem 新增数据库迁移 dotnet ef migrations remove 移除最近一次的数据库迁移 dotnet ef migrations script 生成数据库脚本 mysql -h 124.95.128.166 -P 13306 -u root -p ``` ### 3.3 应用服务类 ##### [强制] 在应用服务项目下使用如下约定定义目录名称 #### 3.3.1. 使用 `IAppService` 目录存放应用服务接口类。 需要继承 `Framework.Core.RegisterInterfaces.IAppService`。 接口中定义方法名时,同步方法以Sync结尾, 异步方法以Async结尾。 #### 3.3.2. 使用 `AppService` 目录存放应用服务接口类。 需要继承 `Framework.Application.Application.Services.AppService`并实现 `IAppService` 定义的接口。 ##### [强制] 在应用服务项目下使用EF查询数据时,禁止在查询表达式中对数据库实体类属性使用类型转换 ```C# // good var equipId = long.Parse(dto.EquipId); var cmsDataTransportEntity = UnitOfWork.GetDBContext().Set().FirstOrDefault(w=>w.EquipId == equipId && w.ProcessStatus == 0); // bad var cmsDataTransportEntity = UnitOfWork.GetDBContext().Set().FirstOrDefault(w=>w.EquipId.ToString().Equals(dto.EquipId) && w.ProcessStatus == 0); ``` #### 3.3.3. 使用 `Dto` 目录存放应用服务中的数据传输对象。 ### 3.4 控制器 ##### [强制] 在控制器项目下使用如下约定定义目录名称 ##### [建议] 在控制器的接口中,直接调用应用服务, 不将业务代码写入控制器的方法. #### 3.4.1. 使用 `Attributes` 目录存放应用服务接口类。 需要继承 `Framework.Core.RegisterInterfaces.IAppService`。 接口中定义方法名时,同步方法以Sync结尾, 异步方法以Async结尾。 #### 3.4.2. 使用 `Controller` 目录存放控制器类。 需要继承 `BaseController`,当继承`BaseController`后,控制器自动对数据的数据进行包装,统一输出格式。如无需对数据数据进行封装,可使用 `IgnoreWrappedApiAttribute` 注解。 #### 3.4.3. 使用 `Filter` 目录存放过滤器,用于实现用户鉴权,业务日志等功能。 ### 3.5 AspNet-MVC宿主服务 ##### [强制] 在AspNet-MVC宿主服务项目下使用如下约定定义目录名称 #### 3.5.1. 使用 `Controller` 目录存放诸如`Consul`健康检测的控制器。 #### 3.5.2. 使用 `Extensions` 目录存放扩展类。 如:`Consul`健康检测,`CorssDomain`跨域, `RabbitMQ`消息队列等。 #### 3.5.3. 使用 `Filter` 目录存放过滤器。 #### 3.5.4. 使用 `Job` 目录存放过定时任务类。 #### 3.5.5. 使用 `RabbitMQHostService` 目录存放过消息队列的消费者实现类。