zoukankan      html  css  js  c++  java
  • AspnetBoilerplate (ABP) Organization Units 组织结构管理

    ABP是一个成熟的.NET框架,功能完善。目前由于项目需要正在自学中。

    ABP对于组织节点管理这一基本上每个项目都要反复重复开发的内容,进行了自己的实现。

    主要包括这些常用功能:

    • 多租户
    • 树结构管理的实体
    • 与用户系统集成的查询

    下面我们根据官方文档以及DEMO去学习这个功能。

    不过需要注意的是,ABP默认没有提供展示层的实现,这一块就需要自己实现了。

    官方文档理解

     OrganizationUnit 实体定义

    • TenantId: 租户ID,如果为null则是host的组织节点。(具体概念参阅多租户)
    • ParentId: 父节点Id,如果为null则是根节点。
    • Code: 一个拼接的虚拟路径字符串代码,在租户内唯一。
    • DisplayName: 显示名称

    Organization Tree

    模型定义中的ParentId使得这个数据结构定义了一个典型的父子树。

    • 这个树允许有多个根节点
    • 树的最大深度是OrganizationUnit.MaxDepth,值为16
    • 每一级子节点的数目也有限制,主要是由于后面要提到的OU Code定义决定的。

    OU Code

    OU Code由OrganizationUnit Manager自动维护,它是类似于"00001.00042.00005"的字符串。它可以用于递归查询。

    这种字段在树结构中是很必须的,如果没有它,树查询会变成效率的杀手。有了这类虚拟路径,可以通过分隔符分解后批量查询。

    Abp对OU Code有以下规则:

    •  在一个租户中唯一
    • 子节点的Code需要以父节点的Code开头
    • Code的长度,由层级深度决定
    • OU Code可以被改变,例如移动节点
    • 我们需要使用Id作为OU引用的字段,而不是Code

    OrganizationUnit Manager

    • OrganizationUnitManager 通过依赖注入引入,一般用于:
    • 增、删、改OU
    • 移动OU
    • 读取OU信息,以及OU的items

    Multi-Tenancy

    OrganizationUnitManager 一次只能操作一个租户,默认租户为当前租户。

    样例代码分析

    首先创建一个实体,派生自IMustHaveTenant , IMustHaveOrganizationUnit

    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; }
    }
    

      

    实现Service:

    public class ProductManager : IDomainService
    {
    	//实体仓储,实体继承自IMustHaveOrganizationUnit
    	private readonly IRepository<Product> _productRepository;
    	//OU仓储,通过此仓储读取OU
    	private readonly IRepository<OrganizationUnit, long> _organizationUnitRepository;
    	//用户数据Manager
    	private readonly UserManager _userManager;
    
    	//构造函数,DI注入
    	public ProductManager(
    		IRepository<Product> productRepository, 
    		IRepository<OrganizationUnit, long> organizationUnitRepository, 
    		UserManager userManager)
    	{
    		_productRepository = productRepository;
    		_organizationUnitRepository = organizationUnitRepository;
    		_userManager = userManager;
    	}
    
    	//根据组织节点,获取关联的Product
    	public List<Product> GetProductsInOu(long organizationUnitId)
    	{
    		return _productRepository.GetAllList(p => p.OrganizationUnitId == organizationUnitId);
    	}
    	
    	//根据组织节点Id查询所有的Products,包含子Product
    	[UnitOfWork]//UnitOfWork支持事务
    	public virtual List<Product> GetProductsInOuIncludingChildren(long organizationUnitId)
    	{
    		//根据组织节点id,获取code
    		var code = _organizationUnitRepository.Get(organizationUnitId).Code;
    
    		//查询组织节点开头的所有节点,这样避免了递归查询,提升了效率,也是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();
    	}
    
    	//根据用户查询Product
    	//查询用户关联的组织节点,再根据组织节点,查询关联的Product
    	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));
    	}
    
    	//同上个函数类似,查询中加入了子节点
    	[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();
    	}
    }
    

      

      

    通过这段源码我们发现,其实在Abp模板中Zero模块已经默认添加了用户与组织节点的关联,如下图:

    OrganizationUnits表是一个父子树结构,表达了我们系统中所有需要以父子树表达的逻辑结构。

    实体表User,Product通过一张关联表与组织节点关联,关联关系如E-R图所示。

    在数据库中,abp并没有创建外键联系,这应该是为了高复用OU表。

    其他设置

    你可以通过 AbpZeroSettingNames.OrganizationUnits.MaxUserMembershipCount 来设置一个用户的最大OU关联数。

  • 相关阅读:
    Console
    在IOS10系统中无法定位问题
    c# js 时间
    ConcurrentDictionary,ConcurrentStack,ConcurrentQueue
    转 控件拖动 未验证
    消息处理模拟点击
    C ProcessAsUser
    SessionChange
    installer
    mysql主从同步配置
  • 原文地址:https://www.cnblogs.com/wswind/p/10176201.html
Copyright © 2011-2022 走看看