0.前言
在《asp.net core 系列》之实战系列中,我们在之前的篇幅中对项目有了一个大概的认知,也搭建了一个基础的项目骨架。那么就让我们继续完善这个骨架,让它更加丰满。这一篇,我将带领小伙伴们一起实现用户管理功能。
1. 数据表
一般情况下,我们会把用户表和登录信息表放在两个表里。为什么会这样设计呢?出于以下几种考虑:
- 使功能分割,用户信息管理是用户管理,登录是登录
- 增加安全,降低无关信息的查询,例如访问登录接口不会连带检索用户的普通信息,当进行用户信息管理的时候,不会把登录信息也带过来
等等
废话不多说,直接上代码:
namespace Data.Enums{/// <summary>/// 登录类型/// </summary>public enum LoginType : byte{/// token登录Token,/// 用户名密码Password}/// <summary>/// 性别/// </summary>public enum SexEnum{/// 男Male,/// 女Female,/// 隐私None}}
SysUserAuthEntity.cs
using Data.Enums;using Data.Infrastructure;namespace Data.Models{public class SysUserAuthEntity : BaseEntity<int>{public string UserName { get; set; }public string Password { get; set; }public LoginType LoginType { get; set;}}}
SysUserInfoEntity.cs
using System;using Data.Enums;using Data.Infrastructure;namespace Data.Models{public class SysUserInfoEntity : BaseEntity<int>{public string NickName { get; set; }public string ImageUrl { get; set; }public SexEnum Sex { get; set; }public DateTime? BirthDay { get; set; }public int SysUserAuthId { get; set; }public virtual SysUserAuthEntity SysUserAuth { get; set; }}}
这里并没有使用数据库Sql语句作为数据库描述,而是使用了Entity类作为描述,这是因为数据库到实体类之间还是有一层转换,对于开发而言接触更多的是实体类,而不是数据表。
2. 生成 Repository相关
使用工具代码的方式有很多,我在这里推荐一种, Test项目中,添加一个测试类,具体代码如下:
using NUnit.Framework;using System;using System.Collections.Generic;using System.Text;using Utils.Develop;namespace Test{public class DevelopTest{[Test]public void TetDevelop(){var d = Develop.CurrentDirect;Console.WriteLine(d);Assert.IsTrue(d.Contains("template"));var entities = Develop.LoadEntities();foreach (var item in entities){Console.WriteLine(item.FullName);}}[Test]public void TestCreateDevelop(){var entities = Develop.LoadEntities();foreach (var item in entities){Develop.CreateRepositoryInterface(item);Develop.CreateRepositoryImplement(item);Develop.CreateEntityTypeConfig(item);}Assert.Pass();}}}
具体的命令行执行比较麻烦,会执行所有的测试单元:
cd Test/dotnet test
当然了,IDE支持单个测试单元的执行,具体操作这里就不做过多的介绍了。
3. Service 接口和实现类
通常Service接口会提供一个简单Crud的Service接口,然后其他业务接口继承该接口。这样可以减少代码的重复,因为重复的代码在开发过程中是非常讨厌的一种情况,因为一旦一处发生变更,其他的也有可能发生变更。所以遇到重复代码一般都会进行一定程度的封装:
using System;using System.Collections.Generic;using System.Linq.Expressions;using Data.Infrastructure;namespace Service.Insfrastructure{public interface BaseService<T>{T Get(object key);T Get(Expression<Func<T, bool>> predicate);PageModel<T> SearchPage(PageCondition<T> condition);void Delete(Expression<Func<T, bool>> predicate);void Update(T entity);List<T> Search(Expression<Func<T, bool>> predicate);}}
暂时就提供了这些最常见的请求方法。
在Service.Implements项目中:
using System;using System.Collections.Generic;using System.Linq.Expressions;using Data.Infrastructure;using Domain.Insfrastructure;using Service.Insfrastructure;namespace Service.Implements.Insfrastructure{public abstract class BaseServiceImpl<T> : BaseService<T>{private IRepository<T> LocalRepository { get; }protected BaseServiceImpl(IRepository<T> repository){LocalRepository = repository;}public T Get(object key){return LocalRepository.Get(key);}public T Get(Expression<Func<T, bool>> predicate){return LocalRepository.Get(predicate);}public PageModel<T> SearchPage(PageCondition<T> condition){return LocalRepository.Search(condition);}public void Delete(Expression<Func<T, bool>> predicate){LocalRepository.Delete(predicate);}public void Update(T entity){LocalRepository.Update(entity);}public List<T> Search(Expression<Func<T, bool>> predicate){return LocalRepository.Search(predicate);}}}
这个类设置为抽象类。
4. 用户管理的接口
先创建了两个简单的示范接口:
using Data.Models;using Service.Insfrastructure;namespace Service{public interface ISysUserService : BaseService<SysUserInfoEntity>{void Register(SysUserAuthEntity auth, SysUserInfoEntity info);void ChangePassword(int userId, string oldPwd, string newPwd);}}
实现类:
using System;using Data.Models;using Domain.Repository;using Service.Implements.Insfrastructure;namespace Service.Implements{public class SysUserServiceImpl : BaseServiceImpl<SysUserInfoEntity>, ISysUserService{protected ISysUserAuthRepository AuthRepository { get; }protected ISysUserInfoRepository InfoRepository { get; }public SysUserServiceImpl(ISysUserAuthRepository authRepository, ISysUserInfoRepository infoRepository) : base(infoRepository){AuthRepository = authRepository;InfoRepository = infoRepository;}public void Register(SysUserAuthEntity auth, SysUserInfoEntity info){var authItem = AuthRepository.Get(p => p.LoginType == auth.LoginType && p.UserName == auth.UserName);if (authItem != null){throw new Exception("用户信息已经存在");}info.SysUserAuth = auth;InfoRepository.Insert(info);}public void ChangePassword(int userId, string oldPwd, string newPwd){var info = InfoRepository.Get(userId);var auth = AuthRepository.Get(info.SysUserAuthId);if (old56cPwd == null || oldPwd != auth?.Password){throw new Exception("原密码错误");}auth.Password = newPwd;}}}
这里没对密码进行加密处理,直接使用明文。这在正式开发中是不允许的,密码不能使用明文保存。当然,这也不是最终代码,下一篇我们将介绍一下.net core中常见的加密实现。
5. 总结
这一篇通过几个简单的示例为大家介绍了一下Service层的开发逻辑以及理念。下一篇将为大家介绍一下.net core中几种简单的加密实现。
更多内容烦请关注我的博客《高先生小屋》