CoreCRM 开发实录 —— Profile
再简单的功能,也需要一坨代码的支持。Profile 的编辑功能主要就是修改个人的信息。比如用户名、头像、性别、电话……虽然只是一个编辑界面,但添加下来,涉及了6个文件的修改和7个新创建的文件。各种生成的和手写的代码,共有934行之多。
1. Account 和 Profile 分离
什么是 Account?通常这个词被翻译成“帐户”。我的理解是,这个 Model 里的内容,是为了登录而设计的。而 Profile 呢,应该保存那些和登录无关的附加信息,比如昵称、头像之类的。不过,有一点坑的是,ASP.NET Core 默认的那个 Identify 服务把电话号码也做为 Account 的一部分,但没有提供通过电话来查找一个 User 的方法。虽然可以通过写一个 Extension 来实现这个功能,暂时先放在一边,有需求再说。
在原来的“悟空CRM”中,所有的信息都是放在 user 这个表里的。对于 CRM 这个应用,也不是一个大的问题。但我认为还是应该把 Profile 和 Account 分开。这样,在登录及验证用户权限的时候,就不需要去访问不相关的 Profile 里的内容。也许有朋友会说:我通过 SELECT 那些我需要的列,不也一样可以实现这个结果吗?但实际上,一个 Row 的内容,都是保存在一起的。只是数据库帮你过滤出来那些需要的信息。但在读取的时候,还是要去访问一整个 Row 的内容。如果分开当然就完全不需要访问了(虽然还有个 ID 在那里,但一个 ID 最多才4个字节)。
2. Repository
Repository 模式其实算是对一些使用 Plain Object 做 ORM 的系统的补充。按照 MVC 的实践准则,业务逻辑应该是写在 Model 这一层的。但为了保持 Model 是一个 Plain Object,只能再引入一层抽象,用来嫁接 Controller 和底层的 Model。当然,使用 Repository 还有另外一个好处,就是把数据库隔离开了。这样,对于 Controller 的单元测试就方便多了。否则,对 Controller 里一些逻辑的测试,还需要配置数据库。不过,有得就一定会有失。这里虽然测试 Controller 方便了,确还得对 Repository 本身时行测试。比较幸运的是,Entity Framework Core 提供了一个 InMemory 的数据库,专门用来测试对数据库的访问。
通常 Repository 会包含以下这些方法:
IEnumerable<Model> GetInstances();
Model GetModelByID(int ID);
void InsertModel(Model model);
void DeleteModel(int ID);
void UpdateModel(Model model);
void Save();
当然,对于具体的某个哪个 Model,这个列表也可以做一些修改。比如对于 Profile 这个 Model,我只使用了:
Task<Profile> GetCurrentUserProfileAsync(ClaimsPrincipal userClaimsPrincipal);
Task<ProfileViewModel> GetCurrentUserProfileViewModelAsync(ClaimsPrincipal userClaimsPrincipal);
Task UpdateProfileAsync(ClaimsPrincipal userClaimsPrincipal, ProfileViewModel model);
感觉上,这三个函数应该做一些修改。因为这个页面不只可以编辑自己的资料,也可以让管理员改任何人的资料。如果是这样,GetCurrentUserProfileAsync
这个函数就应该改成 GetUserProfileAsync
,可以加一个参数:userId
。如果 userId
是 null
的话,就需要获取当前的用户的 Profile
,如果有 userId
, 那就去获取对应用户的 Profile
。
MSDN 上有一篇关于 Repository 模式的说明。写的比较细。园子里的这篇也写的不错。
3. Interface
C# 里通常都是面向 interface 来写程序的。一开始我不是很适应这种模式,因为同一个类,还需要再写一个 interface,等于要把所有的函数签名再抄一遍。如果要修改,也是需要两处都改。保如果把测试考虑进去的话,就不一样了。如果是对一个类产生依赖,那么注入的时候,就需要对这个类进行 mock。如果这个类还有依赖,那整个就悲剧了。如果是使用 interface,这个问题就不存在了。
其实 interface 和 C 语言的 header file 很像。都是定义了函数的签名,然后在另一个地方来实现这个函数的功能。到了 C# 里,为什么这种分享确成了一种负担呢?只是这里有一个问题:interface 是这种侵入式的语言特性。也就是说:一个类必需要继承了对应的 interface 才可以做为这个 interface 的实例来使用。如果想对一个没有修改方法(比如一个 NuGet 包)想添加 interface 的话,就需要一个中间的 Adaptor 来转换。
4. MaxLength & StringLength
EF Core 的 Model 可以通过 Attribute 来指定一个字段的长度。同时,在 ViewModel 里,还可以指定 UI 层的验证。这时候就有两个东东出来了:StringLength 和 MaxLength。其中 MaxLength 是用来指定 EF 里面一个 string 字段的最多可以写多少个字符,也就是:varchar(n)
里的 n。而 StringLength 则是用来指定 ViewModel 里的验证长度的。
下一步
下一步,我要给 ProfileController
增加单元测试,把一些逻辑从 Repository 里弄出来,让 Repository 里尽量少的包含逻辑代码。这样,因为是直通底层,只需要最后做集成测试就可以了,就不需要做单元测试了。
CoreCRM 开发实录 —— Profile的更多相关文章
- CoreCRM 开发实录 —— 单元测试、测试驱动开发和在线服务
测试不是问题,问题是怎么测试. ## 单元测试 我认为单元测试已经是无可争议的最佳开发实践之一.但是很多人并不同意这个观点.他们的说法无非是:写测试需要花很多时间,需求又经常变动,一但变动,一大片测试 ...
- CoreCRM 开发实录——Travis-CI 实现 .NET Core 程度在 macOS 上的构建和测试 [无水干货]
上一篇文章我提到:为了使用"国货",我把 Linux 上的构建和测试委托给了 DaoCloud,而 Travis-CI 不能放着不用啊.还好,这货支持 macOS 系统.所以就把 ...
- CoreCRM 开发实录——开始之新项目的技术选择
2016年11月,接受了一个工作,是对"悟空CRM"进行一些修补.这是一个不错的 CRM,开源,并提供一个 SaaS 的服务.正好微软的 .NET Core 和 ASP.NET C ...
- CoreCRM 开发实录 —— 单元测试之 Mock UserManager 和 SignInManager
单元测试的核心就是:只测试眼前的逻辑.这就要求所有的依赖项都要使用仿类来代替,也就是所谓的 Mock Object.在测试 ProfileRepository 和 AccountController ...
- CoreCRM 开发实录 —— 前后端分离的重构
虽然2月初就回来了,可 CoreCRM 一直到5月才开始恢复开发,期间是各种生活中的意外和不方便. 1. 为什么要重构 首先是一件很值得高兴的事情:CoreCRM 有了第一位 contributor! ...
- CoreCRM 开发实录——想用国货不容易
昨天(2016年12月29日)发了开始开发的文章.本来晚上准备在 Coding.NET 上添加几个任务开始搞起了.可是真的开始用的时候才发现:Coding.NET 的任务功能只针对私有的任务开放.我想 ...
- CoreCRM 开发实录 —— 基于 AntDesign 的新 UI
上一篇说到,因为有新朋友加入,对前端开发有了新的要求.原来基于 Bootstrap 的 UI 就不要了.在网上(其实是 GitHub 上)逛了几圈,最后使用了 antd-admin 这个框架做为基础模 ...
- 【视频开发】ONVIF、RTSP/RTP、FFMPEG的开发实录
ONVIF.RTSP/RTP.FFMPEG的开发实录 前言 本文从零基础一步步实现ONVIF协议.RTSP/RTP协议获取IPC实时视频流.FFMPEG解码.开发环境为WIN7 32位 + VS201 ...
- Google Chrome Native Messaging开发实录(二)Chrome Extension扩展
接上一篇<Google Chrome Native Messaging开发实录(一)背景介绍>的项目背景,话不多说,有关Chrome Extension介绍和文档就不展开了,直接上代码. ...
随机推荐
- 对抗密码破解 —— Web 前端慢 Hash
(更新:https://www.cnblogs.com/index-html/p/frontend_kdf.html ) 0x00 前言 天下武功,唯快不破.但在密码学中则不同.算法越快,越容易破. ...
- 隐私泄露杀手锏 —— Flash 权限反射
[简版:http://weibo.com/p/1001603881940380956046] 前言 一直以为该风险早已被重视,但最近无意中发现,仍有不少网站存在该缺陷,其中不乏一些常用的邮箱.社交网站 ...
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- 【腾讯Bugly干货分享】Android Linker 与 SO 加壳技术
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57e3a3bc42eb88da6d4be143 作者:王赛 1. 前言 Andr ...
- FullCalendar应用——整合农历节气和节日
FullCalendar用来做日程管理功能非常强大,但是唯一不足的地方是没有将中国农历历法加进去,今天我将结合实例和大家分享如何将中国农历中的节气和节日整合到FullCalendar中,从而增强其实用 ...
- Autofac - 生命周期
实例生命周期决定在同一个服务的每个请求的实例是如何共享的. 当请求一个服务的时候,Autofac会返回一个单例 (single instance作用域), 一个新的对象 (per lifetime作用 ...
- isEmpty和isNull()区别
isEmpty和isNull()区别一个NULL字符串一定是一个空串,一个空串未必是一个NULL字符串例如:QString().isNull(): //结果为trueQString().isEm ...
- ABP创建数据库操作步骤
1 ABP创建数据库操作步骤 1.1 SimpleTaskSystem.Web项目中的Web.config文件修改数据库配置. <add name="Default" pro ...
- 学习C的笔记
[unsigned] 16位系统中一个int能存储的数据的范围为-32768~32767,而unsigned能存储的数据范围则是0~65535.由于在计算机中,整数是以补码形式存放的.根据最高位的不同 ...
- oracle常用函数及示例
学习oracle也有一段时间了,发现oracle中的函数好多,对于做后台的程序猿来说,大把大把的时间还要学习很多其他的新东西,再把这些函数也都记住是不太现实的,所以总结了一下oracle中的一些常用函 ...