仓储层 Repository 实现
1.1 创建 Repository 项目
登录接口,我们用到的:登录 API,我们可以直接注入使用,这是因为 Identity,已经帮我们内置好了。
但是我们的权限相关功能,需要操作的数据库的,就需要我们自己实现。
创建项目 =》选择类库 =》输入 Electric.Repository。
框架:选择. Net 7.0。
创建完成
默认生成的 Class1.cs 可以删除。
1.2 添加类
以获取登录用户的权限列表为例,下面我们来实现,相关数据库操作功能。
1.2.1、添加类 RolePermissionRepositiory
public class RolePermissionRepositiory
{
}
1.2.2、添加项目引用
因为我们需要操作数据库,所以需要先添加项目引用:Electric.DbMigrator
<ItemGroup>
<ProjectReference Include="..\Electric.DbMigrator\Electric.DbMigrator.csproj" />
</ItemGroup>
1.2.3、注入数据库上下文
Web API 是支持依赖注入的,所以数据库上下文,直接在构造函数定义就可以。实现如下:
public class RolePermissionRepositiory
{
private readonly ApplicationDbContext _dbContext;
public RolePermissionRepositiory(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
}
1.3 RolePermissionRepositiory 接口的实现
添加方法 GetRolePermissions,根据角色名称,获取权限列表
/// <summary>
/// 获取角色权限列表
/// </summary>
/// <param name="roleName"></param>
/// <returns></returns>
public List<AccountPermissionsDto> GetRolePermissions(string roleName)
{
var permissionsDtos = new List<AccountPermissionsDto>(permissions);
return permissionsDtos;
}
2.1 提取接口
根据以上的步骤,我们已经定义好了:RolePermissionRepositiory
。但是我们要使用依赖注入,那就需要提取接口。
接口代码如下:
public interface IRolePermissionRepositiory
{
/// <summary>
/// 获取角色权限列表
/// </summary>
/// <param name="roleName"></param>
/// <returns></returns>
List<AccountPermissionsDto> GetRolePermissions(string roleName);
}
改为继承接口 IRolePermissionRepositiory,完整代码如下:
public class RolePermissionRepositiory : IRolePermissionRepositiory
{
private readonly ApplicationDbContext _dbContext;
public RolePermissionRepositiory(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public List<AccountPermissionsDto> GetRolePermissions(string roleName)
{
var permissionsDtos = new List<AccountPermissionsDto>(permissions);
return permissionsDtos;
}
}
2.2 添加项目依赖
先为 Eletric.API 添加项目依赖:Electric.Repository
<ProjectReference Include="..\Electric.Repository\Electric.Repository.csproj" />
Repository 依赖注入
2.3 项目入口添加依赖
在 Program.cs,添加如下代码,实现依赖注入。
builder.Services.AddTransient(typeof(IRolePermissionRepositiory), typeof(RolePermissionRepositiory));
依赖注入有 3 中方法:AddTransient、AddScoped、AddSingleton,区别如下:
-
AddTransient:无论是不是同一个请求(同一个请求里的不同服务)同一个客户端,每次都是创建新的实例;
-
AddScoped: 对于同一个请求返回同一个实例,不同的请求返回不同的实例;
-
AddSingleton: 每次都是获得同一个实例, 单一实例模式。
2.4 RolePermissionRepositiory 依赖注入使用
在构造函数注入依赖:IRolePermissionRepositiory,这样我们就访问数据库了。
同时我们还添加其他两个依赖:IHttpContextAccessor、UserManager
IHttpContextAccessor 这个是 Http 上下文,因为我们获取登录信息,所以要添加这个注入,同时这个是 Web API 默认注入的,我们可以直接使用。
public class AccountsController : ControllerBase
{
private readonly IRolePermissionRepositiory _rolePermissionRepositiory;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserManager<EleUser> _userManager;
public AccountsController(IRolePermissionRepositiory rolePermissionRepositiory, IHttpContextAccessor httpContextAccessor, UserManager<EleUser> userManager)
{
_rolePermissionRepositiory = rolePermissionRepositiory;
_httpContextAccessor = httpContextAccessor;
_userManager = userManager;
}
}
2.5 实现接口功能
我们在数据库注入后,我们就可以直接使用了,使用方式如下:
完整代码如下:
[HttpGet("permissions")]
public async Task<IActionResult> GetPermissions()
{
var userName = _httpContextAccessor.HttpContext.User.Identity.Name;
var entity = await _userManager.FindByNameAsync(userName);
var roles = await _userManager.GetRolesAsync(entity);
var rolePermissions = new Dictionary<long, AccountPermissionsDto>();
foreach (var roleName in roles)
{
var _rolePermissions = _rolePermissionRepositiory.GetRolePermissions(roleName);
foreach (var permission in _rolePermissions)
{
if(!rolePermissions.ContainsKey(permission.Id))
{
rolePermissions.Add(permission.Id, permission);
}
}
}
var list = rolePermissions.Values.ToList();
return Ok(list);
}
2.6 测试接口
运行项目,测试下 GetPermissions 接口,就能看到 Http 状态码为 200,可以正常返回。
说明:依赖注入是成功的,这边返回空列表,这是因为我们 Repository 还未实现相关功能。
2.7 重构依赖注入
通过上面步骤的介绍,我们通过以下代码就可以完成依赖注入。
但是,如果未来我们的业务系统,功能越来越多,就会有很多相关的 Repository,这样就需要每个都要注入。
所以,我们可以采用反射机制,来自动获取并注入。
先定义一个父类:BaseRepository
namespace Electric.Repository
{
public class BaseRepository
{
}
}
让所有 Repositiory 继承 BaseRepository。
并在项目入口 Program.cs,修改依赖注入代码:
//添加注入
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach(var assembly in assemblies)
{
//获取继承BaseRepository的类
List<Type> types = assembly.GetTypes()
.Where(t => t.BaseType == typeof(BaseRepository))
.ToList();
types.ForEach(impl =>
{
//获取该类所继承的所有接口
Type[] interfaces = impl.GetInterfaces();
interfaces.ToList().ForEach(i =>
{
builder.Services.AddTransient(i, impl);
});
});
}
如果我们此时,运行项目,进行用户信息接口测试,会发现会报错。
这是因为我们,在项目入口的注入这边,GetAssemblies 只获取使用过的程序集,未使用的程序集不会包含其中,所以我们要进行修改。
先添加 AssemblyExtension 扩展。
using System.Reflection;
namespace Electric.API.Extensions
{
/// <summary>
/// Assembly扩展
/// </summary>
public static class AssemblyExtension
{
/// <summary>
/// 获取所有程序集
/// </summary>
/// <param name="domain"></param>
/// <returns></returns>
public static List<Assembly> GetReferanceAssemblies(this AppDomain domain)
{
var list = new List<Assembly>();
domain.GetAssemblies().ToList().ForEach(i =>
{
GetReferanceAssemblies(i, list);
});
return list;
}
static void GetReferanceAssemblies(Assembly assembly, List<Assembly> list)
{
assembly.GetReferencedAssemblies().ToList().ForEach(i =>
{
var ass = Assembly.Load(i);
if (!list.Contains(ass))
{
list.Add(ass);
GetReferanceAssemblies(ass, list);
}
});
}
}
}
在项目入口,修改注入,GetAssemblies 修改为 GetReferanceAssemblies。
再次运行项目,测试下 GetPermissions 接口,就能看到 Http 状态码为 200,可以正常返回。
说明:依赖注入是成功的。
Automapper映射
我们上面的步骤,并未真正的实现数据库操作,下面一起来完成,并演示如何使用 AutoMapper。
3.1 先添加引用 AutoMapper
先为项目 Electric.Entity,添加引用:AutoMapper,版本 12.0.1。
<PackageReference Include="AutoMapper" Version="12.0.1" />
3.2 创建映射对象关系
以下代码创建映射关系:ElePermission 可以转化为 AccountPermissionsDto。
如果要 AccountPermissionsDto 可以转化为 ElePermission,要添加对应代码:
CreateMap<AccountPermissionsDto, ElePermission>();
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<ElePermission, AccountPermissionsDto>();
}
public static IMapper CreateMapper()
{
//创建配置对象
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
//创建映射对象
var mapper = configuration.CreateMapper();
return mapper;
}
}
3.3 实现接口功能
以下代码,实现:从数据库获取权限列表,并转化对象。
public List<AccountPermissionsDto> GetRolePermissions(string roleName)
{
//根据角色获取权限列表
var permissions = (from p in _dbContext.ElePermission
join rp in _dbContext.EleRolePermission on p.Id equals rp.PermissionId
join r in _dbContext.Roles on rp.RoleId equals r.Id
where r.NormalizedName == roleName
select p
).ToList();
//对象映射转化
var permissionsDtos = _mapper.Map<List<AccountPermissionsDto>>(permissions);
return permissionsDtos;
}
运行项目,在浏览器测试接口,结果如下,可以正确的返回权限列表。
3.4 重构为依赖注入
我们在项目实际开发中,可能会创建多个映射对象关系【见 3.2】。
这就会导致我们需要了解多个映射对象关系的内容,我们可以改为依赖注入,这样方便我们使用。
3.4.1 项目入口添加依赖注入
在 Program.cs,添加如下代码:
AutoMapper.IConfigurationProvider config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<MappingProfile>();
});
builder.Services.AddSingleton(config);
builder.Services.AddScoped<IMapper, Mapper>();
3.4.2 添加注入对象
在 Repositiory 的构造函数添加注入 IMapper
public class RolePermissionRepositiory : BaseRepository, IRolePermissionRepositiory
{
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
public RolePermissionRepositiory(ApplicationDbContext dbContext, IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
}
3.4.3 修改接口功能
修改:获取角色权限列表【见 3.3】,代码如下:
public List<AccountPermissionsDto> GetRolePermissions(string roleName)
{
var permissions = (from p in _dbContext.ElePermission
join rp in _dbContext.EleRolePermission on p.Id equals rp.PermissionId
join r in _dbContext.Roles on rp.RoleId equals r.Id
where r.NormalizedName == roleName
select p
).ToList();
var permissionsDtos = _mapper.Map<List<AccountPermissionsDto>>(permissions);
return permissionsDtos;
}
运行项目,在浏览器测试接口,结果如下,可以正确的返回权限列表。
测试获取用户信息,结果如下,可以正常返回: