标签 标签 标签


0.前言

在《asp.net core 系列》之实战系列中,我们在之前的篇幅中对项目有了一个大概的认知,也搭建了一个基础的项目骨架。那么就让我们继续完善这个骨架,让它更加丰满。这一篇,我将带领小伙伴们一起实现用户管理功能。

1. 数据表

一般情况下,我们会把用户表和登录信息表放在两个表里。为什么会这样设计呢?出于以下几种考虑:

  • 使功能分割,用户信息管理是用户管理,登录是登录
  • 增加安全,降低无关信息的查询,例如访问登录接口不会连带检索用户的普通信息,当进行用户信息管理的时候,不会把登录信息也带过来

等等
废话不多说,直接上代码:

  1. namespace Data.Enums
  2. {
  3. /// <summary>
  4. /// 登录类型
  5. /// </summary>
  6. public enum LoginType : byte
  7. {
  8. /// token登录
  9. Token,
  10. /// 用户名密码
  11. Password
  12. }
  13. /// <summary>
  14. /// 性别
  15. /// </summary>
  16. public enum SexEnum
  17. {
  18. /// 男
  19. Male,
  20. /// 女
  21. Female,
  22. /// 隐私
  23. None
  24. }
  25. }

SysUserAuthEntity.cs

  1. using Data.Enums;
  2. using Data.Infrastructure;
  3. namespace Data.Models
  4. {
  5. public class SysUserAuthEntity : BaseEntity<int>
  6. {
  7. public string UserName { get; set; }
  8. public string Password { get; set; }
  9. public LoginType LoginType { get; set;}
  10. }
  11. }

SysUserInfoEntity.cs

  1. using System;
  2. using Data.Enums;
  3. using Data.Infrastructure;
  4. namespace Data.Models
  5. {
  6. public class SysUserInfoEntity : BaseEntity<int>
  7. {
  8. public string NickName { get; set; }
  9. public string ImageUrl { get; set; }
  10. public SexEnum Sex { get; set; }
  11. public DateTime? BirthDay { get; set; }
  12. public int SysUserAuthId { get; set; }
  13. public virtual SysUserAuthEntity SysUserAuth { get; set; }
  14. }
  15. }

这里并没有使用数据库Sql语句作为数据库描述,而是使用了Entity类作为描述,这是因为数据库到实体类之间还是有一层转换,对于开发而言接触更多的是实体类,而不是数据表。

2. 生成 Repository相关

使用工具代码的方式有很多,我在这里推荐一种, Test项目中,添加一个测试类,具体代码如下:

  1. using NUnit.Framework;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using Utils.Develop;
  6. namespace Test
  7. {
  8. public class DevelopTest
  9. {
  10. [Test]
  11. public void TetDevelop()
  12. {
  13. var d = Develop.CurrentDirect;
  14. Console.WriteLine(d);
  15. Assert.IsTrue(d.Contains("template"));
  16. var entities = Develop.LoadEntities();
  17. foreach (var item in entities)
  18. {
  19. Console.WriteLine(item.FullName);
  20. }
  21. }
  22. [Test]
  23. public void TestCreateDevelop()
  24. {
  25. var entities = Develop.LoadEntities();
  26. foreach (var item in entities)
  27. {
  28. Develop.CreateRepositoryInterface(item);
  29. Develop.CreateRepositoryImplement(item);
  30. Develop.CreateEntityTypeConfig(item);
  31. }
  32. Assert.Pass();
  33. }
  34. }
  35. }

具体的命令行执行比较麻烦,会执行所有的测试单元:

  1. cd Test/
  2. dotnet test

当然了,IDE支持单个测试单元的执行,具体操作这里就不做过多的介绍了。

3. Service 接口和实现类

通常Service接口会提供一个简单Crud的Service接口,然后其他业务接口继承该接口。这样可以减少代码的重复,因为重复的代码在开发过程中是非常讨厌的一种情况,因为一旦一处发生变更,其他的也有可能发生变更。所以遇到重复代码一般都会进行一定程度的封装:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using Data.Infrastructure;
  5. namespace Service.Insfrastructure
  6. {
  7. public interface BaseService<T>
  8. {
  9. T Get(object key);
  10. T Get(Expression<Func<T, bool>> predicate);
  11. PageModel<T> SearchPage(PageCondition<T> condition);
  12. void Delete(Expression<Func<T, bool>> predicate);
  13. void Update(T entity);
  14. List<T> Search(Expression<Func<T, bool>> predicate);
  15. }
  16. }

暂时就提供了这些最常见的请求方法。
在Service.Implements项目中:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using Data.Infrastructure;
  5. using Domain.Insfrastructure;
  6. using Service.Insfrastructure;
  7. namespace Service.Implements.Insfrastructure
  8. {
  9. public abstract class BaseServiceImpl<T> : BaseService<T>
  10. {
  11. private IRepository<T> LocalRepository { get; }
  12. protected BaseServiceImpl(IRepository<T> repository)
  13. {
  14. LocalRepository = repository;
  15. }
  16. public T Get(object key)
  17. {
  18. return LocalRepository.Get(key);
  19. }
  20. public T Get(Expression<Func<T, bool>> predicate)
  21. {
  22. return LocalRepository.Get(predicate);
  23. }
  24. public PageModel<T> SearchPage(PageCondition<T> condition)
  25. {
  26. return LocalRepository.Search(condition);
  27. }
  28. public void Delete(Expression<Func<T, bool>> predicate)
  29. {
  30. LocalRepository.Delete(predicate);
  31. }
  32. public void Update(T entity)
  33. {
  34. LocalRepository.Update(entity);
  35. }
  36. public List<T> Search(Expression<Func<T, bool>> predicate)
  37. {
  38. return LocalRepository.Search(predicate);
  39. }
  40. }
  41. }

这个类设置为抽象类。

4. 用户管理的接口

先创建了两个简单的示范接口:

  1. using Data.Models;
  2. using Service.Insfrastructure;
  3. namespace Service
  4. {
  5. public interface ISysUserService : BaseService<SysUserInfoEntity>
  6. {
  7. void Register(SysUserAuthEntity auth, SysUserInfoEntity info);
  8. void ChangePassword(int userId, string oldPwd, string newPwd);
  9. }
  10. }

实现类:

  1. using System;
  2. using Data.Models;
  3. using Domain.Repository;
  4. using Service.Implements.Insfrastructure;
  5. namespace Service.Implements
  6. {
  7. public class SysUserServiceImpl : BaseServiceImpl<SysUserInfoEntity>, ISysUserService
  8. {
  9. protected ISysUserAuthRepository AuthRepository { get; }
  10. protected ISysUserInfoRepository InfoRepository { get; }
  11. public SysUserServiceImpl(ISysUserAuthRepository authRepository, ISysUserInfoRepository infoRepository) : base(
  12. infoRepository)
  13. {
  14. AuthRepository = authRepository;
  15. InfoRepository = infoRepository;
  16. }
  17. public void Register(SysUserAuthEntity auth, SysUserInfoEntity info)
  18. {
  19. var authItem = AuthRepository.Get(p => p.LoginType == auth.LoginType && p.UserName == auth.UserName);
  20. if (authItem != null)
  21. {
  22. throw new Exception("用户信息已经存在");
  23. }
  24. info.SysUserAuth = auth;
  25. InfoRepository.Insert(info);
  26. }
  27. public void ChangePassword(int userId, string oldPwd, string newPwd)
  28. {
  29. var info = InfoRepository.Get(userId);
  30. var auth = AuthRepository.Get(info.SysUserAuthId);
  31. if (oldPwd == null || oldPwd != auth?.Password)
  32. {
  33. throw new Exception("原密码错误");
  34. }
  35. auth.Password = newPwd;
  36. }
  37. }
  38. }

这里没对密码进行加密处理,直接使用明文。这在正式开发中是不允许的,密码不能使用明文保存。当然,这也不是最终代码,下一篇我们将介绍一下.net core中常见的加密实现。

5. 总结

这一篇通过几个简单的示例为大家介绍了一下Service层的开发逻辑以及理念。下一篇将为大家介绍一下.net core中几种简单的加密实现。


  • 本文作者:GeekPower - Felix Sun
  • 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!