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”的接口文件。暂时定义以下几个基本的接口,日后针对批量插入、删除、分页等操作再做进一步的完善。

/// <summary>
/// 仓储接口定义
/// </summary>
public interface IRepository
{ }
/// <summary>
/// 定义泛型仓储接口
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TPrimaryKey">主键类型</typeparam>
public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : Entity<TPrimaryKey>
{
/// <summary>
/// 获取实体集合
/// </summary>
/// <returns></returns>
List<TEntity> GetAllList(); /// <summary>
/// 根据lambda表达式条件获取实体集合
/// </summary>
/// <param name="predicate">lambda表达式条件</param>
/// <returns></returns>
List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate); /// <summary>
/// 根据主键获取实体
/// </summary>
/// <param name="id">实体主键</param>
/// <returns></returns>
TEntity Get(TPrimaryKey id); /// <summary>
/// 根据lambda表达式条件获取单个实体
/// </summary>
/// <param name="predicate">lambda表达式条件</param>
/// <returns></returns>
TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate); /// <summary>
/// 新增实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
TEntity Insert(TEntity entity); /// <summary>
/// 更新实体
/// </summary>
/// <param name="entity">实体</param>
TEntity Update(TEntity entity); /// <summary>
/// 新增或更新实体
/// </summary>
/// <param name="entity">实体</param>
TEntity InsertOrUpdate(TEntity entity); /// <summary>
/// 删除实体
/// </summary>
/// <param name="entity">要删除的实体</param>
bool Delete(TEntity entity); /// <summary>
/// 删除实体
/// </summary>
/// <param name="id">实体主键</param>
bool Delete(TPrimaryKey id);
}

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

/// <summary>
/// 默认Guid主键类型仓储
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : Entity
{ }

1.1 用户管理仓储接口定义

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

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

/// <summary>
/// 用户管理仓储接口
/// </summary>
public interface IUserRepository : IRepository<User>
{
/// <summary>
/// 检查用户是存在
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="password">密码</param>
/// <returns>存在返回用户实体,否则返回NULL</returns>
User CheckUser(string userName, string password);
}

2 仓储接口实现

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

2.0 基本接口实现

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

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

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

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

2 定义了一个Save操作方法

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

/// <summary>
/// 仓储基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
/// <typeparam name="TPrimaryKey">主键类型</typeparam>
public abstract class FonourRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : Entity<TPrimaryKey>
{
//定义数据访问上下文对象
protected readonly FonourDbContext _dbContext; /// <summary>
/// 通过构造函数注入得到数据上下文对象实例
/// </summary>
/// <param name="dbContext"></param>
public FonourRepositoryBase(FonourDbContext dbContext)
{
_dbContext = dbContext;
} /// <summary>
/// 获取实体集合
/// </summary>
/// <returns></returns>
public List<TEntity> GetAllList()
{
return _dbContext.Set<TEntity>().ToList();
} /// <summary>
/// 根据lambda表达式条件获取实体集合
/// </summary>
/// <param name="predicate">lambda表达式条件</param>
/// <returns></returns>
public List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
{
return _dbContext.Set<TEntity>().Where(predicate).ToList();
} /// <summary>
/// 根据主键获取实体
/// </summary>
/// <param name="id">实体主键</param>
/// <returns></returns>
public TEntity Get(TPrimaryKey id)
{
return _dbContext.Set<TEntity>().FirstOrDefault(CreateEqualityExpressionForId(id));
} /// <summary>
/// 根据lambda表达式条件获取单个实体
/// </summary>
/// <param name="predicate">lambda表达式条件</param>
/// <returns></returns>
public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return _dbContext.Set<TEntity>().FirstOrDefault(predicate);
} /// <summary>
/// 新增实体
/// </summary>
/// <param name="entity">实体</param>
/// <returns></returns>
public TEntity Insert(TEntity entity)
{
_dbContext.Set<TEntity>().Add(entity);
return entity;
} /// <summary>
/// 更新实体
/// </summary>
/// <param name="entity">实体</param>
public TEntity Update(TEntity entity)
{
_dbContext.Set<TEntity>().Attach(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
return entity;
} /// <summary>
/// 新增或更新实体
/// </summary>
/// <param name="entity">实体</param>
public TEntity InsertOrUpdate(TEntity entity)
{
if (Get(entity.Id) != null)
return Update(entity);
return Insert(entity);
} /// <summary>
/// 删除实体
/// </summary>
/// <param name="entity">要删除的实体</param>
public void Delete(TEntity entity)
{
_dbContext.Set<TEntity>().Remove(entity);
} /// <summary>
/// 删除实体
/// </summary>
/// <param name="id">实体主键</param>
public void Delete(TPrimaryKey id)
{
_dbContext.Set<TEntity>().Remove(Get(id));
} /// <summary>
/// 事务性保存
/// </summary>
public void Save()
{
_dbContext.SaveChanges();
} /// <summary>
/// 根据主键构建判断表达式
/// </summary>
/// <param name="id">主键</param>
/// <returns></returns>
protected static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
{
var lambdaParam = Expression.Parameter(typeof(TEntity));
var lambdaBody = Expression.Equal(
Expression.PropertyOrField(lambdaParam, "Id"),
Expression.Constant(id, typeof(TPrimaryKey))
); return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
}
}

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

/// <summary>
/// 主键为Guid类型的仓储基类
/// </summary>
/// <typeparam name="TEntity">实体类型</typeparam>
public abstract class FonourRepositoryBase<TEntity> : FonourRepositoryBase<TEntity, Guid> where TEntity : Entity
{
public FonourRepositoryBase(FonourDbContext dbContext) : base(dbContext)
{
}
}

2.1 用户管理仓储接口实现

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

/// <summary>
/// 用户管理仓储实现
/// </summary>
public class UserRepository : FonourRepositoryBase<User>, IUserRepository
{
public UserRepository(FonourDbContext dbcontext) : base(dbcontext)
{ }
/// <summary>
/// 检查用户是存在
/// </summary>
/// <param name="userName">用户名</param>
/// <param name="password">密码</param>
/// <returns>存在返回用户实体,否则返回NULL</returns>
public User CheckUser(string userName, string password)
{
return _dbContext.Set<User>().FirstOrDefault(it => it.UserName == userName && it.Password == password);
}
}

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

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

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

/// <summary>
/// 用户管理服务
/// </summary>
public class UserAppService : IUserAppService
{
//用户管理仓储接口
private readonly IUserRepository _userReporitory; /// <summary>
/// 构造函数 实现依赖注入
/// </summary>
/// <param name="userRepository">仓储对象</param>
public UserAppService(IUserRepository userRepository)
{
_userReporitory = userRepository;
} public User CheckUser(string userName, string password)
{
return _userReporitory.CheckUser(userName, password);
}
}

4 Asp.Net Core依赖注入实现

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

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

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

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

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

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

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

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

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

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

5 测试

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

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

public class LoginController : Controller
{
private IUserAppService _userAppService;
public LoginController(IUserAppService userAppService)
{
_userAppService = userAppService;
}
// GET: /<controller>/
public IActionResult Index()
{
var user = _userAppService.CheckUser("admin", "");
return View();
}
}

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

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. CMS模板应用调研问卷

    截止目前,已经有数十家网站与我们合作,进行了MIP化改造,在搜索结果页也能看到"闪电标"的出现.除了改造方面的问题,MIP项目组被问到最多的就是:我用了wordpress,我用了织 ...

  2. Laravel 5.x 请求的生命周期(附源码)

    Laravel最早接触是刚开始实习的时候,那时通过网上的学习资料很快便上手,开发模块接口.后来没有什么深入和总结,但是当我刚开始学Laravel的时候,我对Laravel最大的认识就是,框架除了路由. ...

  3. Nested Loops join时显示no join predicate原因分析以及解决办法

    本文出处:http://www.cnblogs.com/wy123/p/6238844.html 最近遇到一个存储过程在某些特殊的情况下,效率极其低效, 至于底下到什么程度我现在都没有一个确切的数据, ...

  4. [原创]关于Hibernate中的级联操作以及懒加载

    Hibernate: 级联操作 一.简单的介绍 cascade和inverse (Employee – Department) Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似 ...

  5. HTML5笔记2——HTML5音/视频标签详解

    音视频的发展史 早期:<embed>+<object>+文件 问题:不是所有浏览器都支持,而且embed不是标准. 现状:Realplay.window media.Quick ...

  6. “此网页上的某个 Web 部件或 Web 表单控件无法显示或导入。找不到该类型,或该类型未注册为安全类型。”

    自从vs装了Resharper,看见提示总是手贱的想去改掉它.于是乎手一抖,把一个 可视web部件的命名空间给改了. 喏,从LibrarySharePoint.WebPart.LibraryAddEd ...

  7. Merge Sorted Array

    Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note:Yo ...

  8. 我的MYSQL学习心得(十六) 优化

    我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  9. SurfaceView 绘制分形图

    之前一直做的是应用类,这次抽时间,参考网上资料实践了下SurfaceView.目标是在页面上画一个科赫曲线的分形图. 代码如下: package com.example.fredric.demo02; ...

  10. 基于Quick-cocos2d-x的资源更新方案 二

    写在前面 又是12点半了,对于一个程序员来说,这是一个黄金时间,精力旺盛,我想,是最适合整理和分享一些思路的时候了. 自从上次写了 基于Quick-cocos2d-x的资源更新方案 同样可见quick ...