1.骨架概述
vue.netcore优势在于generator涵盖了api、ui全栈,不能否认的快;‘快’是通过反设计模式、反面向对象提供的畸形的高效率——它利于第一次开发,不利于维护:技术栈特性通常不考虑,只堆不理,对常见需求,如过滤器、管道,破坏性实现
当前阶段强调实现,不考虑已有内容的优化
2.编码规约
2.1命名风格
- 【强制】Pascal命名(namespace、class、property、method)、camel命名(field、route etc..)
- 【强制】杜绝含义不明的缩写、简写(业务缩写除外)
正例:order delete Department
反例:ord del Dept
- 【建议】 为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意
反例:BrandController LocationController (不可读意)
杜绝 int a 的随意命名方式
慎用xxx_Class、xxx_Type、xxx_ClassDetail等直译名称,这些词义已经混淆了
理解需求翻译需求,替换例子:Purpose、Goals、CategoryOne、CategoryTwo、Kind、Property
- 【建议】保持属性的连续可读,推荐 “属性名称_属性状态”的命名方式
正例:ResultExpected ResultActual
C#的语法通常不推荐下划线’_’,是否广泛适用有争议
- 【强制】不允许出现魔幻值,必须包装成枚举、静态类
【建议】各层命名规约:
减少if..elseif…else…等同于积德
-
2.3注释规约
注释的问题永远有完美解决
如果认为代码结构已经不用解释了,那就不要注释,过度注释和没有注释一样
- 合适的标记,fix issue #1;todo;see cref
- 不要在代码同一行注释,也不要一行一注释
2.4数据库
- repository
- xxxRepository.cs的修改记录应该有且只有一条——首次提交
- service
- controller
讨论 既然框架已经不遵循领域模型,那么也没有必要照搬概念 以下的讨论限定在vue.netcore的场景下
前提是维持框架内的运行逻辑,不评论作者的实现 (1)controller service 在实现上只是一套逻辑的写两个地方,在这个概念上针对它们优化的意义不大 (2)强制限制controller内容 (3)service是逻辑垃圾堆,个人认为清理它的最重要是不要往里再倒垃圾——再开个垃圾场 (4)补充数据操作控制力,虽然cms系统事务性不多
3.一期项目其他问题
- web-api
- 谓词
- 框架里本来就乱
- 谓词和方法名的约定
- 路由
- 格式规约,推荐小写,推荐’-‘作为分割
- 参数
- 几乎全是json-body
- 响应
- 弱类型(有包装的有不包装的)
- 暴露了数据库实体
- 错误处理
- 缺乏错误的业务针对性处理(错误包装、错误代码)
- 谓词
- orm-dapper
efcore 不香?
如果必要执行原生SQL,那么这些SQL要么在DB要么在配置文件
- orm-efcore
逐步取消外键和DbEntity.Include
(1)笛卡尔积爆炸(2)无法进行数据表横切
- LINQ
WipOrderService.getChannelAndWipOperation
(1)LINQ可以有效的在保证LINQ2Db的同时提供封装性(如新、旧逻辑交织)
(2)配合automapper,免修改逻辑控制层
4.有必要考虑的补充
- CMS套件
- 缓存设计
- 错误处理
- 程序异常套件,捕捉,日志,包装的友好提示
- 业务异常套件,错误代码,包装的错误提示
- 多语言
- 事务性控制
- 数据库事务,有强制的书写范围,read-commited可以满足绝大多数要求
- 分布式事务,用不着
- 需求控制
[Benchmark] public void LeftOuterJoin() { for (int i = 0; i < N; i++) { using var context = new BloggingContext(); var blogs = context.Blogs .Include(blog => blog.Posts) .ThenInclude(post => post.Tags) .ThenInclude(tags => tags.Tag) .ToList(); } }
[Benchmark] public void LeftOuterJoinVsLinQ() { for (int i = 0; i < N; i++) { using var context = new BloggingContext();
var query = from blog in context.Blogsjoin post in context.Posts on blog.BlogId equals post.BlogId into gfrom post in g.DefaultIfEmpty()join postTag in context.PostTags on post.PostId equals postTag.PostId into hfrom postTag in h.DefaultIfEmpty()join tag in context.Tags on postTag.TagId equals tag.TagId into mfrom tag in m.DefaultIfEmpty()select new{blog,post,postTag,tag};var blogs = query.ToList();}
}
```BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1766 (21H2)Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores.NET SDK=6.0.100[Host] : .NET 6.0.0 (6.0.21.52210), X64 RyuJITDefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT| Method | N | Mean | Error | StdDev ||---------------------- |-- |--------:|---------:|---------:|| LeftOuterJoin | 1 | 4.234 s | 0.0837 s | 0.1990 s || LeftOuterJoinWithLinQ | 1 | 1.961 s | 0.0314 s | 0.0262 s |
SELECT `b`.`BlogId`, `b`.`OwnerId`, `b`.`Rating`, `b`.`ThemeId`, `b`.`Url`, `t1`.`PostId`, `t1`.`AuthorId`, `t1`.`BlogId`, `t1`.`Content`, `t1`.`Rating`, `t1`.`Title`, `t1`.`PostTagId`, `t1`.`PostId0`, `t1`.`TagId`, `t1`.`TagId0`FROM `Blogs` AS `b`LEFT JOIN (SELECT `p`.`PostId`, `p`.`AuthorId`, `p`.`BlogId`, `p`.`Content`, `p`.`Rating`, `p`.`Title`, `t0`.`PostTagId`, `t0`.`PostId` AS `PostId0`, `t0`.`TagId`, `t0`.`TagId0`FROM `Posts` AS `p`LEFT JOIN (SELECT `p0`.`PostTagId`, `p0`.`PostId`, `p0`.`TagId`, `t`.`TagId` AS `TagId0`FROM `PostTags` AS `p0`LEFT JOIN `Tags` AS `t` ON `p0`.`TagId` = `t`.`TagId`) AS `t0` ON `p`.`PostId` = `t0`.`PostId`) AS `t1` ON `b`.`BlogId` = `t1`.`BlogId`ORDER BY `b`.`BlogId`, `t1`.`PostId`, `t1`.`PostTagId`;SELECT
b.BlogId,b.OwnerId,b.Rating,b.ThemeId,b.Url,p.PostId,p.AuthorId,p.BlogId,p.Content,p.Rating,p.Title,p0.PostTagId,p0.PostId,p0.TagId,t.TagIdFROMBlogsASbLEFT JOINPostsASpONb.BlogId=p.BlogIdLEFT JOINPostTagsASp0ONp.PostId=p0.PostIdLEFT JOINTagsAStONp0.TagId=t.TagId;
