再简单的功能,也需要一坨代码的支持。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。如果 userIdnull 的话,就需要获取当前的用户的 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的更多相关文章

  1. CoreCRM 开发实录 —— 单元测试、测试驱动开发和在线服务

    测试不是问题,问题是怎么测试. ## 单元测试 我认为单元测试已经是无可争议的最佳开发实践之一.但是很多人并不同意这个观点.他们的说法无非是:写测试需要花很多时间,需求又经常变动,一但变动,一大片测试 ...

  2. CoreCRM 开发实录——Travis-CI 实现 .NET Core 程度在 macOS 上的构建和测试 [无水干货]

    上一篇文章我提到:为了使用"国货",我把 Linux 上的构建和测试委托给了 DaoCloud,而 Travis-CI 不能放着不用啊.还好,这货支持 macOS 系统.所以就把 ...

  3. CoreCRM 开发实录——开始之新项目的技术选择

    2016年11月,接受了一个工作,是对"悟空CRM"进行一些修补.这是一个不错的 CRM,开源,并提供一个 SaaS 的服务.正好微软的 .NET Core 和 ASP.NET C ...

  4. CoreCRM 开发实录 —— 单元测试之 Mock UserManager 和 SignInManager

    单元测试的核心就是:只测试眼前的逻辑.这就要求所有的依赖项都要使用仿类来代替,也就是所谓的 Mock Object.在测试 ProfileRepository 和 AccountController ...

  5. CoreCRM 开发实录 —— 前后端分离的重构

    虽然2月初就回来了,可 CoreCRM 一直到5月才开始恢复开发,期间是各种生活中的意外和不方便. 1. 为什么要重构 首先是一件很值得高兴的事情:CoreCRM 有了第一位 contributor! ...

  6. CoreCRM 开发实录——想用国货不容易

    昨天(2016年12月29日)发了开始开发的文章.本来晚上准备在 Coding.NET 上添加几个任务开始搞起了.可是真的开始用的时候才发现:Coding.NET 的任务功能只针对私有的任务开放.我想 ...

  7. CoreCRM 开发实录 —— 基于 AntDesign 的新 UI

    上一篇说到,因为有新朋友加入,对前端开发有了新的要求.原来基于 Bootstrap 的 UI 就不要了.在网上(其实是 GitHub 上)逛了几圈,最后使用了 antd-admin 这个框架做为基础模 ...

  8. 【视频开发】ONVIF、RTSP/RTP、FFMPEG的开发实录

    ONVIF.RTSP/RTP.FFMPEG的开发实录 前言 本文从零基础一步步实现ONVIF协议.RTSP/RTP协议获取IPC实时视频流.FFMPEG解码.开发环境为WIN7 32位 + VS201 ...

  9. Google Chrome Native Messaging开发实录(二)Chrome Extension扩展

    接上一篇<Google Chrome Native Messaging开发实录(一)背景介绍>的项目背景,话不多说,有关Chrome Extension介绍和文档就不展开了,直接上代码. ...

随机推荐

  1. 记一个mvn奇怪错误: Archive for required library: 'D:/mvn/repos/junit/junit/3.8.1/junit-3.8.1.jar' in project 'xxx' cannot be read or is not a valid ZIP file

    我的maven 项目有一个红色感叹号, 而且Problems 存在 errors : Description Resource Path Location Type Archive for requi ...

  2. 探索ASP.NET MVC5系列之~~~4.模型篇---包含模型常用特性和过度提交防御

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  3. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之目录导航

    ASP.NET MVC with Entity Framework and CSS是2016年出版的一本比较新的.关于ASP.NET MVC.EF以及CSS技术的图书,我将尝试着翻译本书以供日后查阅. ...

  4. Ajax实现原理,代码封装

    都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...

  5. C#中将DataTable导出为HTML的方法

    今天我要向大家分享一种将DataTable导出为到HTML格式的方法.有时我们需要HTML格式的输出数据, 以下代码就可以帮助我们达到目的,. 首先,我们要绑定DataTable和 DataGridV ...

  6. mongodb

    修改所有的记录: > db.t_express_apply.update({},{$set:{"isStatus" : 0}},{multi:true})WriteResul ...

  7. Node.js学习笔记——Node.js开发Web后台服务

    一.简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.Node.j ...

  8. Flex 布局教程:语法篇

    作者: 阮一峰 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便 ...

  9. 2016/12/30_Python

    今天主要学习内容: Python: 1.字典的使用 1)怎么创建字典 dicts = {"name":"juncx","age":17} d ...

  10. atitit.细节决定成败的适合情形与缺点

    atitit.细节决定成败的适合情形与缺点 1. 在理论界有两种观点:一种是"细节决定成败",另一种是"战略决定成败".1 1.1. 格局决定成败,方向决定成败 ...