## 2.4 ABPZero - 组织单位管理
### 2.4.1 简介
组织单位(OU)用于给组用户和实体分层
### 2.4.2 组织单位实体
一个OU由组织单位实体来展现。该实体基本的属性如下:
+ TenantId:OU的租户Id,host的组织单位的TenantId可以为空。
+ ParentId:父OU的Id,根OU的ParentId可以为空。
+ Code:租户下唯一的层级字符串。
+ DisplayName:OU的显示名称。
组织单位实体的主键(Id)是长整型,该实体从FullAuditedEntity继承,FullAuditedEntity提供了审计信息并实现了软删除接口。
#### 1. 组织单位树
由于一个OU可以有父级OU,一个租户下所有的OU是树形结构,该树有如下规则:
+ 可以有多个根节点(根节点的ParentId为空)
+ 树的最大深度被定义为一个常量OrganizationUnit.MaxDepth,它的值是16。
+ 第一层级子OU数有限制(由于固定的OU编码单位长度的原因,下面有相关解释)
#### 2. OU编码
OU编码有组织单位管理器自动创建和维护,OU编码是类似下面的字符串:
"00001.00042.00005"
该编码可以轻易实现递归查询数据库中所有的子OU,该编码有如下规则:
+ 对于某个租户来说,它是唯一的。
+ 一个父OU的所有子OU的编码都以父OU的编码开头。
+ 它的固定长度基于树中的OU层级决定,参见示例代码。
+ 尽管OU编码是唯一的,但是在你移动一个OU的情况下它能被改变,因此我们应该通过OU的Id来引用OU,而不是OU的编码。
### 2.4.3 组织单位管理器
组织单位管理器类可以被注入并用于管理OU,常见用例如下:
+ 创建、更新、删除一个OU。
+ 在OU树中移动一个OU。
+ 获取OU树及节点的信息。
#### 1. 多租户
组织单位管理器被设计用来为单租户使用,它默认为当前租户使用。
### 2.4.4 常见用例
以下是OU的常见用例,相关示例的源代码[请点这里](https://github.com/aspnetboilerplate/aspnetboilerplate-samples/tree/master/OrganizationUnitsDemo)。
#### 1. 创建属于某个组织单位的实体
OU最明显的用例是将实体分配给某个OU,例如:
```csharp
public class Product : Entity, IMustHaveTenant, IMustHaveOrganizationUnit
{
public virtual int TenantId { get; set; }
public virtual long OrganizationUnitId { get; set; }
public virtual string Name { get; set; }
public virtual float Price { get; set; }
}
```
我们简单地创建了OrganizationUnitId这个属性用于将实体分配给某个OU。IMustHaveOrganizationUnit接口定义了OrganizationUnitId属性。
我们不是必须要实现这个接口,但是它被建议实现来作为一个标准。另外还有一个IMayHaveOrganizationId接口,这个接口定义了可为空的OrganizationUnitId属性。
现在我们就可以把一个产品关联到某个OU并且查询某个指定OU的相关产品了。
注意:在多租户应用中,产品实体拥有一个TenantId属性(它是IMustHaveTenant接口的一个属性)来区分不同租户的产品。如果你的应用不是多租户的应用,你就不需要这个接口及其相关的属性。
#### 2. 获取某个组织单位内的实体
获取某个组织单位内的实体很简单,请看如下领域服务中的示例代码:
```csharp
public class ProductManager : IDomainService
{
private readonly IRepository<Product> _productRepository;
public ProductManager(IRepository<Product> productRepository)
{
_productRepository = productRepository;
}
public List<Product> GetProductsInOu(long organizationUnitId)
{
return _productRepository.GetAllList(p => p.OrganizationUnitId == organizationUnitId);
}
}
```
像上面代码里那样,我们只需要简单地加一个谓词Product.OrganizationUnitId。
#### 3. 获取某个组织单位及其子组织单位内的实体
当我们想获取某个组织单位及其子组织单位内的实体的时候,OU编码就可以帮我们实现这个功能了。
```csharp
public class ProductManager : IDomainService
{
private readonly IRepository<Product> _productRepository;
private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
public ProductManager(
IRepository<Product> productRepository,
IRepository<OrganizationUnit, long> organizationUnitRepository)
{
_productRepository = productRepository;
_organizationUnitRepository = organizationUnitRepository;
}
[UnitOfWork]
public virtual List<Product> GetProductsInOuIncludingChildren(long organizationUnitId)
{
var code = _organizationUnitRepository.Get(organizationUnitId).Code;
var query =
from product in _productRepository.GetAll()
join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
where organizationUnit.Code.StartsWith(code)
select product;
return query.ToList();
}
}
```
首先,我们获取指定的OU的编码,然后我们创建一个带join和StartsWith(code)条件的LINQ查询(StartsWith创建了sql里的like查询)。因此,我们能够向下按层级获取一个OU及其子OU所属的产品。
#### 4. 过滤某个用户的实体
我们可能希望获取某个用户所属的OU(一个或多个)内的所有产品,代码如下:
```csharp
public class ProductManager : IDomainService
{
private readonly IRepository<Product> _productRepository;
private readonly UserManager _userManager;
public ProductManager(
IRepository<Product> productRepository,
UserManager userManager)
{
_productRepository = productRepository;
_organizationUnitRepository = organizationUnitRepository;
_userManager = userManager;
}
public async Task<List<Product>> GetProductsForUserAsync(long userId)
{
var user = await _userManager.GetUserByIdAsync(userId);
var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
var organizationUnitIds = organizationUnits.Select(ou => ou.Id);
return await _productRepository.GetAllListAsync(p => organizationUnitIds.Contains(p.OrganizationUnitId));
}
}
```
我们通过简单地查询获取了用户所属OU的Id列表,然后我们使用Contains条件来查询产品。当然,我们可以创建LINQ关联查询来获取相同的列表。
我们也可能会希望获取某个用户所属的OU(一个或多个)内及其子OU内的所有产品
```csharp
public class ProductManager : IDomainService
{
private readonly IRepository<Product> _productRepository;
private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
private readonly UserManager _userManager;
public ProductManager(
IRepository<Product> productRepository,
IRepository<OrganizationUnit, long> organizationUnitRepository,
UserManager userManager)
{
_productRepository = productRepository;
_organizationUnitRepository = organizationUnitRepository;
_userManager = userManager;
}
[UnitOfWork]
public virtual async Task<List<Product>> GetProductsForUserIncludingChildOusAsync(long userId)
{
var user = await _userManager.GetUserByIdAsync(userId);
var organizationUnits = await _userManager.GetOrganizationUnitsAsync(user);
var organizationUnitCodes = organizationUnits.Select(ou => ou.Code);
var query =
from product in _productRepository.GetAll()
join organizationUnit in _organizationUnitRepository.GetAll() on product.OrganizationUnitId equals organizationUnit.Id
where organizationUnitCodes.Any(code => organizationUnit.Code.StartsWith(code))
select product;
return query.ToList();
}
}
```
我们在LINQ关联查询声明中组合使用了Any和StartsWith条件。
当然,可能会有更复杂的需求,但是都能用LINQ和SQL来实现。
### 2.4.5 设置
我们可以注入并使用IOrganizationUnitSettings接口来获取组织单位设置值。当前,只有一个设置能被按需改变:
+ MaxUserMembershipCount:一个用户所属组织单位的数量的最大值。
+ 默认值是int.MaxValue,即无限制。
+ 设置的名称是一个常量,被定义在AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount中。
你可以通过[设置管理](/Markdown/Abp/2.5ABP公共结构-设置管理.md)改变设定值
>苏州Andrew
- 文章&教程
- Abp
- 1.1ABP总体介绍-入门介绍
- 1.2ABP总体介绍-多层架构体系
- 1.3ABP总体介绍-模块系统
- 1.4ABP总体介绍-启动配置
- 1.5ABP总体介绍-多租户
- 1.6ABP总体介绍-集成OWIN
- 2.1ABP公共结构-依赖注入
- 2.2ABP公共结构-会话管理
- 2.3ABP公共结构-缓存管理
- 2.4ABP公共结构-日志管理
- 2.5ABP公共结构-设置管理
- 2.6ABP公共结构-时区设置
- 3.1ABP领域层-实体
- 3.2ABP领域层-值对象
- 3.3ABP领域层-仓储
- 3.4ABP领域层-领域服务
- 3.5ABP领域层-工作单元
- 3.6ABP领域层-领域事件
- 3.7ABP领域层-数据过滤器
- 4.1ABP应用层-应用服务
- 4.2ABP应用层-数据传输对象
- 4.3ABP应用层-数据传输对象验证
- 4.4ABP应用层-权限认证
- 4.5ABP应用层-功能管理
- 4.6ABP应用层-审计日志
- 5.1ABP分布式服务-ASP.NET WebApi
- 5.2ABP分布式服务-动态WebApi层
- 5.3ABP分布式服务-集成OData
- 5.4ABP分布式服务-集成SwaggerUI
- 6.1ABP表现层-Mvc控制器
- 6.2ABP表现层-Mvc视图
- 6.3ABP表现层-本地化
- 6.4ABP表现层-导航栏
- 6.5ABP表现层-异常处理
- 6.6.2-AJAX
- 6.6.3-Notification
- 6.6.4-Message
- 6.6.5-UIBlockBusy
- 6.6.6-EventBus
- 6.6.7-Logging
- 6.6.8-OtherUtilities
- 6.6ABP表现层-JavascriptAPI
- 6.7ABP表现层-嵌入资源文件
- 6.8ASP.NET-Core
- 6.9CSRF和XSRF保护
- 7.1ABP后台服务-后台作业和工人
- 7.2ABP后台服务-集成Hangfire
- 8.1ABP实时服务-通知系统
- 8.2ABP实时服务-集成SignalR
- 9.1ABP基础设施层-集成Entity Framework
- 9.2ABP基础设施层-集成NHibernate
- 9.3ABP基础设施层-集成Entity Framework Core
- abp合并版160929
- md文档排版规范
- AbpZero
- 1.1ABPZero-概述
- 1.2ABPZero-安装
- 1.3ABPZero-启动模板
- 1.4ABPZero-启动模板Core
- 2.1ABPZero-多租户管理
- 2.2ABPZero-版本管理
- 2.4ABPZero-组织单位管理