0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有

1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端

2 Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计

3 Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL

4 Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现

5 Asp.Net Core 项目实战之权限管理系统(5) 用户登录

6 Asp.Net Core 项目实战之权限管理系统(6) 功能管理

7 Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限

8 Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载

github源码地址

0 项目结构

写这个系列的最初目的其实只是为了自己能更好的学习Asp.Net Core,用一个小的系统作为练习,也督促自己。短期内不见得在实际项目中真的会运用,但至少通过学习,大致的对Asp.Net Core有个了解,也是为以后可能的应用做一下技术储备。起初的设想很简单,就是在一个Web项目中完成所有工作,大致了解Asp.Net Core的知识体系。

在实践的过程中不自觉的对这个练习的项目进行了一下分层。目前项目整体机构如下:

项目说明:

  • Fonour.MVC

Asp.Net Core MVC网站项目。

  • Fonour.Application

      应用服务项目,定义应用服务接口及实现,供Fonour.MVC控制器调用;同时定义接收及返回数据对象(Dto,这里我有可能会省去,直接拿实体往表现层传了……)

  • Fonour.Domain

主要定义实体、仓储接口等。

  • Fonour.EntityFrameworkCore

主要是仓储接口的EF Core具体实现

  • Fonour.Utility

      通用项目,定义项目无关的一些公共类库。

1 仓储接口定义

1.0 基本接口定义

仓储接口定义使用泛型接口,主要定义实体基本的增、删、改、查操作。在Fonour.Domain项目中新建一个名称为“IRepositories”的文件夹,在该文件夹中新建一个名称为“IRepository”的接口文件。暂时定义以下几个基本的接口,日后针对批量插入、删除、分页等操作再做进一步的完善。

  1. /// <summary>
  2. /// 仓储接口定义
  3. /// </summary>
  4. public interface IRepository
  5. {
  6.  
  7. }
  8. /// <summary>
  9. /// 定义泛型仓储接口
  10. /// </summary>
  11. /// <typeparam name="TEntity">实体类型</typeparam>
  12. /// <typeparam name="TPrimaryKey">主键类型</typeparam>
  13. public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : Entity<TPrimaryKey>
  14. {
  15. /// <summary>
  16. /// 获取实体集合
  17. /// </summary>
  18. /// <returns></returns>
  19. List<TEntity> GetAllList();
  20.  
  21. /// <summary>
  22. /// 根据lambda表达式条件获取实体集合
  23. /// </summary>
  24. /// <param name="predicate">lambda表达式条件</param>
  25. /// <returns></returns>
  26. List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
  27.  
  28. /// <summary>
  29. /// 根据主键获取实体
  30. /// </summary>
  31. /// <param name="id">实体主键</param>
  32. /// <returns></returns>
  33. TEntity Get(TPrimaryKey id);
  34.  
  35. /// <summary>
  36. /// 根据lambda表达式条件获取单个实体
  37. /// </summary>
  38. /// <param name="predicate">lambda表达式条件</param>
  39. /// <returns></returns>
  40. TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
  41.  
  42. /// <summary>
  43. /// 新增实体
  44. /// </summary>
  45. /// <param name="entity">实体</param>
  46. /// <returns></returns>
  47. TEntity Insert(TEntity entity);
  48.  
  49. /// <summary>
  50. /// 更新实体
  51. /// </summary>
  52. /// <param name="entity">实体</param>
  53. TEntity Update(TEntity entity);
  54.  
  55. /// <summary>
  56. /// 新增或更新实体
  57. /// </summary>
  58. /// <param name="entity">实体</param>
  59. TEntity InsertOrUpdate(TEntity entity);
  60.  
  61. /// <summary>
  62. /// 删除实体
  63. /// </summary>
  64. /// <param name="entity">要删除的实体</param>
  65. bool Delete(TEntity entity);
  66.  
  67. /// <summary>
  68. /// 删除实体
  69. /// </summary>
  70. /// <param name="id">实体主键</param>
  71. bool Delete(TPrimaryKey id);
  72. }

这个练习项目使用的是Guid类型的主键,为方便使用,再继承定义一个主键类型为Guid的接口。

  1. /// <summary>
  2. /// 默认Guid主键类型仓储
  3. /// </summary>
  4. /// <typeparam name="TEntity"></typeparam>
  5. public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : Entity
  6. {
  7.  
  8. }

1.1 用户管理仓储接口定义

如无特殊操作需要,我们的基础接口基本上能够满足各类实体共性的增删改查操作。对于某种实体特有的操作,就需要单独进行操作接口的定义。比如后面我们要实现用户登录的验证功能,即提供用户名、密码,验证该用户是否存在,以及用户名密码是否正确。对于此类特定需求,我们针对用户实体定义一个用户管理的仓储接口。

在“IRepositories”文件夹下新建一个名称为“IUserRepository”的接口,里面暂时只定义一个检查用户是否存在的方法,做为我们最后的测试接口。

  1. /// <summary>
  2. /// 用户管理仓储接口
  3. /// </summary>
  4. public interface IUserRepository : IRepository<User>
  5. {
  6. /// <summary>
  7. /// 检查用户是存在
  8. /// </summary>
  9. /// <param name="userName">用户名</param>
  10. /// <param name="password">密码</param>
  11. /// <returns>存在返回用户实体,否则返回NULL</returns>
  12. User CheckUser(string userName, string password);
  13. }

2 仓储接口实现

仓储接口的实现全部放在Fonour.EntityFrameworkCore项目中,通过EF Core使用PostgresSQL数据库实现。

2.0 基本接口实现

在Fonour.EntityFrameworkCore项目中新建一个名称为“Repositories”的文件夹,在文件夹中添加一个名称为“FonourRepositoryBase”的抽象类。

该抽象类除了实现基本仓储接口定义的方法外,还有2个需要注意的地方。、

1 定义了数据访问上下文对象

该数据访问上下文对象通过构造函数进行依赖注入,Asp.Net Core已经默认对数据访问上下文对象进行了构造函数依赖注入的实现,具体应用后面会说到。

2 定义了一个Save操作方法

目的为了在应用服务层调用多个仓储后,统一进行数据上下文的SaveChanges操作,保证数据存储的事务性。

  1. /// <summary>
  2. /// 仓储基类
  3. /// </summary>
  4. /// <typeparam name="TEntity">实体类型</typeparam>
  5. /// <typeparam name="TPrimaryKey">主键类型</typeparam>
  6. public abstract class FonourRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : Entity<TPrimaryKey>
  7. {
  8. //定义数据访问上下文对象
  9. protected readonly FonourDbContext _dbContext;
  10.  
  11. /// <summary>
  12. /// 通过构造函数注入得到数据上下文对象实例
  13. /// </summary>
  14. /// <param name="dbContext"></param>
  15. public FonourRepositoryBase(FonourDbContext dbContext)
  16. {
  17. _dbContext = dbContext;
  18. }
  19.  
  20. /// <summary>
  21. /// 获取实体集合
  22. /// </summary>
  23. /// <returns></returns>
  24. public List<TEntity> GetAllList()
  25. {
  26. return _dbContext.Set<TEntity>().ToList();
  27. }
  28.  
  29. /// <summary>
  30. /// 根据lambda表达式条件获取实体集合
  31. /// </summary>
  32. /// <param name="predicate">lambda表达式条件</param>
  33. /// <returns></returns>
  34. public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
  35. {
  36. return _dbContext.Set<TEntity>().Where(predicate).ToList();
  37. }
  38.  
  39. /// <summary>
  40. /// 根据主键获取实体
  41. /// </summary>
  42. /// <param name="id">实体主键</param>
  43. /// <returns></returns>
  44. public TEntity Get(TPrimaryKey id)
  45. {
  46. return _dbContext.Set<TEntity>().FirstOrDefault(CreateEqualityExpressionForId(id));
  47. }
  48.  
  49. /// <summary>
  50. /// 根据lambda表达式条件获取单个实体
  51. /// </summary>
  52. /// <param name="predicate">lambda表达式条件</param>
  53. /// <returns></returns>
  54. public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
  55. {
  56. return _dbContext.Set<TEntity>().FirstOrDefault(predicate);
  57. }
  58.  
  59. /// <summary>
  60. /// 新增实体
  61. /// </summary>
  62. /// <param name="entity">实体</param>
  63. /// <returns></returns>
  64. public TEntity Insert(TEntity entity)
  65. {
  66. _dbContext.Set<TEntity>().Add(entity);
  67. return entity;
  68. }
  69.  
  70. /// <summary>
  71. /// 更新实体
  72. /// </summary>
  73. /// <param name="entity">实体</param>
  74. public TEntity Update(TEntity entity)
  75. {
  76. _dbContext.Set<TEntity>().Attach(entity);
  77. _dbContext.Entry(entity).State = EntityState.Modified;
  78. return entity;
  79. }
  80.  
  81. /// <summary>
  82. /// 新增或更新实体
  83. /// </summary>
  84. /// <param name="entity">实体</param>
  85. public TEntity InsertOrUpdate(TEntity entity)
  86. {
  87. if (Get(entity.Id) != null)
  88. return Update(entity);
  89. return Insert(entity);
  90. }
  91.  
  92. /// <summary>
  93. /// 删除实体
  94. /// </summary>
  95. /// <param name="entity">要删除的实体</param>
  96. public void Delete(TEntity entity)
  97. {
  98. _dbContext.Set<TEntity>().Remove(entity);
  99. }
  100.  
  101. /// <summary>
  102. /// 删除实体
  103. /// </summary>
  104. /// <param name="id">实体主键</param>
  105. public void Delete(TPrimaryKey id)
  106. {
  107. _dbContext.Set<TEntity>().Remove(Get(id));
  108. }
  109.  
  110. /// <summary>
  111. /// 事务性保存
  112. /// </summary>
  113. public void Save()
  114. {
  115. _dbContext.SaveChanges();
  116. }
  117.  
  118. /// <summary>
  119. /// 根据主键构建判断表达式
  120. /// </summary>
  121. /// <param name="id">主键</param>
  122. /// <returns></returns>
  123. protected static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
  124. {
  125. var lambdaParam = Expression.Parameter(typeof(TEntity));
  126. var lambdaBody = Expression.Equal(
  127. Expression.PropertyOrField(lambdaParam, "Id"),
  128. Expression.Constant(id, typeof(TPrimaryKey))
  129. );
  130.  
  131. return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
  132. }
  133. }

同样的实现一个主键类型为Guid的仓储操作基类。

  1. /// <summary>
  2. /// 主键为Guid类型的仓储基类
  3. /// </summary>
  4. /// <typeparam name="TEntity">实体类型</typeparam>
  5. public abstract class FonourRepositoryBase<TEntity> : FonourRepositoryBase<TEntity, Guid> where TEntity : Entity
  6. {
  7. public FonourRepositoryBase(FonourDbContext dbContext) : base(dbContext)
  8. {
  9. }
  10. }

2.1 用户管理仓储接口实现

在Fonour.EntityFrameworkCore项目的“Repositories”文件夹中新建一个用户管理仓储接口的实现类“UserRepository”,实现接口中定义的用户检查方法。

  1. /// <summary>
  2. /// 用户管理仓储实现
  3. /// </summary>
  4. public class UserRepository : FonourRepositoryBase<User>, IUserRepository
  5. {
  6. public UserRepository(FonourDbContext dbcontext) : base(dbcontext)
  7. {
  8.  
  9. }
  10. /// <summary>
  11. /// 检查用户是存在
  12. /// </summary>
  13. /// <param name="userName">用户名</param>
  14. /// <param name="password">密码</param>
  15. /// <returns>存在返回用户实体,否则返回NULL</returns>
  16. public User CheckUser(string userName, string password)
  17. {
  18. return _dbContext.Set<User>().FirstOrDefault(it => it.UserName == userName && it.Password == password);
  19. }
  20. }

3 应用服务层接口定义及实现

在Fonour.Application项目中新建一个名称为“UserApp”的文件夹,文件夹中新建一个名称为“IUserAppService”的服务接口,及服务接口的具体实现“UserAppService”,在服务层中调用对应的仓储方法实现具体相关业务逻辑。

UserAppService中定义一个私有且只读的用户管理仓储接口对象,依然通过构造函数的方式进行依赖注入。

  1. /// <summary>
  2. /// 用户管理服务
  3. /// </summary>
  4. public class UserAppService : IUserAppService
  5. {
  6. //用户管理仓储接口
  7. private readonly IUserRepository _userReporitory;
  8.  
  9. /// <summary>
  10. /// 构造函数 实现依赖注入
  11. /// </summary>
  12. /// <param name="userRepository">仓储对象</param>
  13. public UserAppService(IUserRepository userRepository)
  14. {
  15. _userReporitory = userRepository;
  16. }
  17.  
  18. public User CheckUser(string userName, string password)
  19. {
  20. return _userReporitory.CheckUser(userName, password);
  21. }
  22. }

4 Asp.Net Core依赖注入实现

在上一节中,我们讲到在Fonour.MVC项目的Startup.cs文件的ConfigureServices方法中通过使用

  1. services.AddDbContext<FonourDbContext>(options =>options.UseNpgsql(sqlConnectionString));

方法将数据库上上下文添加到系统服务中,正是在此时同时对数据访问上下文进行了依赖注入实现。

通过添加以下代码在ConfigureServices方法中添加对上面创建的仓储及服务进行依赖注入的实现。

  1. services.AddScoped<IUserRepository, UserRepository>();
  2. services.AddScoped<IUserAppService, UserAppService>();

注意:Asp.Net Core提供的依赖注入拥有三种生命周期模式,由短到长依次为:

  • Transient     ServiceProvider总是创建一个新的服务实例。
  • Scoped         ServiceProvider创建的服务实例由自己保存,(同一次请求)所以同一个ServiceProvider对象提供的服务实例均是同一个对象。
  • Singleton      始终是同一个实例对象

对于数据访问上下文,我们可以通过重载方法的第二个参数,控制数据访问上下文对象的生命周期,默认生命周期为Scoped。

  1. services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Transient);
  2. services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Scoped);
  3. services.AddDbContext<FonourDbContext>(options => options.UseNpgsql(sqlConnectionString), ServiceLifetime.Singleton);

对于要依赖注入的接口和对象提供AddTransient、AddScoped、AddSingleton三个方法控制对象声明周期。

5 测试

我们在Fonour.MVC项目的LoginController中增加一个IUserAppService服务对象的定义,同时提供LoginController的构造函数,在构造函数中实现对UserAppService服务的依赖注入。

在Index控制器中增加IUserAppService的用户检查方法的调用代码,增加一个断点,用于测试。

  1. public class LoginController : Controller
  2. {
  3. private IUserAppService _userAppService;
  4. public LoginController(IUserAppService userAppService)
  5. {
  6. _userAppService = userAppService;
  7. }
  8. // GET: /<controller>/
  9. public IActionResult Index()
  10. {
  11. var user = _userAppService.CheckUser("admin", "");
  12. return View();
  13. }
  14. }

运行程序,进入断点,发现已经成功根据用户名和密码,把上一节创建的用户数据信息取出,至此,我们项目的分层之间通道已经打通。

6 总结

本节主要涉及到Asp.Net Core的知识点是它的依赖注入机制,我们通过清晰多项目分层结构,采用依赖注入机制,实现了各通之间的连接。

下一节实现用户登录相关,主要有用户登录验证,以及用户对控制器Action路由访问的拦截及判断。

Asp.Net Core 项目实战之权限管理系统(4) 依赖注入、仓储、服务的多项目分层实现的更多相关文章

  1. Asp.Net Core 项目实战之权限管理系统(0) 无中生有

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  2. Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  3. Asp.Net Core 项目实战之权限管理系统(2) 功能及实体设计

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  4. Asp.Net Core 项目实战之权限管理系统(3) 通过EntityFramework Core使用PostgreSQL

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  5. Asp.Net Core 项目实战之权限管理系统(5) 用户登录

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  6. Asp.Net Core 项目实战之权限管理系统(6) 功能管理

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  7. Asp.Net Core 项目实战之权限管理系统(7) 组织机构、角色、用户权限

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  8. Asp.Net Core 项目实战之权限管理系统(8) 功能菜单的动态加载

    0 Asp.Net Core 项目实战之权限管理系统(0) 无中生有 1 Asp.Net Core 项目实战之权限管理系统(1) 使用AdminLTE搭建前端 2 Asp.Net Core 项目实战之 ...

  9. Net Core 项目实战之权限管理系统(0)

    0 前言 Net Core 项目实战之权限管理系统(0) 无中生有   0 http://www.cnblogs.com/fonour/p/5848933.html 学习的最好方法就是动手去做,这里以 ...

随机推荐

  1. iOS代码规范(OC和Swift)

    下面说下iOS的代码规范问题,如果大家觉得还不错,可以直接用到项目中,有不同意见 可以在下面讨论下. 相信很多人工作中最烦的就是代码不规范,命名不规范,曾经见过一个VC里有3个按钮被命名为button ...

  2. Microservice Anti-patterns

    在最近的一次Microservices Practitioner Summit中,原Netflix工程师介绍了一种越来越常见的对Microservice的误用.简单地说,大家在搭建一个基于Micros ...

  3. 使用 .NET WinForm 开发所见即所得的 IDE 开发环境,实现不写代码直接生成应用程序

    直接切入正题,这是我09年到11年左右业余时间编写的项目,最初的想法很简单,做一个能拖拖拽拽就直接生成应用程序的工具,不用写代码,把能想到的业务操作全部封装起来,通过配置的方式把这些业务操作组织起来运 ...

  4. 用CIL写程序:你好,沃尔德

    前言: 项目紧赶慢赶总算在年前有了一些成绩,所以沉寂了几周之后,小匹夫也终于有时间写点东西了.以前匹夫写过一篇文章,对CIL做了一个简单地介绍,不过不知道各位看官看的是否过瘾,至少小匹夫觉得很不过瘾. ...

  5. C++中的事件分发

    本文意在展现一个C++实现的通用事件分发系统,能够灵活的处理各种事件.对于事件处理函数的注册,希望既能注册到普通函数,注册到事件处理类,也能注册到任意类的成员函数.这样在游戏客户端的逻辑处理中,可以非 ...

  6. .NET平台开源项目速览(15)文档数据库RavenDB-介绍与初体验

    不知不觉,“.NET平台开源项目速览“系列文章已经15篇了,每一篇都非常受欢迎,可能技术水平不高,但足够入门了.虽然工作很忙,但还是会抽空把自己知道的,已经平时遇到的好的开源项目分享出来.今天就给大家 ...

  7. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

  8. BlockingCollection使用

    BlockingCollection是一个线程安全的生产者-消费者集合. 代码 public class BlockingTest { BlockingCollection<int> bc ...

  9. python 3.5 成功安装 scrapy 的步骤

    http://www.cnblogs.com/hhh5460/p/5814275.html

  10. GJM : C#设计模式汇总整理——导航 【原创】

    感谢您的阅读.喜欢的.有用的就请大哥大嫂们高抬贵手"推荐一下"吧!你的精神支持是博主强大的写作动力以及转载收藏动力.欢迎转载! 版权声明:本文原创发表于 [请点击连接前往] ,未经 ...