背景:上一篇中我们重写了UserManager类,本文中我们对OrganizationUnitManager动手,原因呢,当然还是封装的方法中有一些不必要的验证,当然像组织机构名这种几乎不会重复的,但是它限制我们不让我们重复,就对它很不爽(开玩笑),这里主要目的,还是去掉不必要 的校验,添加一些辅助方法,方便我们更好 的使用abp框架。
1.在Domain层创建OrganizationManager类
这里我们继承OrganizationUnitManager(这里没有对应的泛型类),同样是因为不必重写所有的方法,我们就挑有校验的方法下手。
创建机构
public override async Task CreateAsync(OrganizationUnit organizationUnit)
{
await SetCodeAsync(organizationUnit);
await this.OrganizationUnitRepository.InsertAsync(organizationUnit).ConfigureAwait(false);
}
public async Task SetCodeAsync(OrganizationUnit organizationUnit,string code = "")
{
var pf = GetPropertyInfo("Code");
if (pf!=null)
{
if (string.IsNullOrEmpty(code))
{
pf.SetValue(organizationUnit, await this.GetNextChildCodeAsync(organizationUnit.ParentId).ConfigureAwait(false));
}
else
{
pf.SetValue(organizationUnit, code);
}
}
}
private PropertyInfo GetPropertyInfo(string fieldName)
{
Type t = typeof(OrganizationUnit);
return t.GetProperties().FirstOrDefault(s => s.Name == fieldName);
}
从封装代码上来看OrganizationUnitManager类比UserManager要简单很多。
这里我们添加了一个辅助方法SetCodeAsync,因为Code字段set是internal的,这里之所以用反射来赋值,是因为在
OrganizationUnitManager和OrganizationUnit类中都没有找到给Code赋值的方法,而源码里也只有创建,移动单位的时候才会内部调用赋值,但我们重写就没办法了,目前只能选择这种方式赋值。
同时,将SetCodeAsync设置为public,方便以后调用。
更新机构
这里我们没有选择重写,因为去掉校验步骤,其实源码里调用的就是OrganizationUnitRepository.UpdateAsync,没有重写的必要。
移动机构:
[UnitOfWork]
public override async Task MoveAsync(Guid id, Guid? parentId)
{
OrganizationUnit organizationUnit1 = await this.OrganizationUnitRepository.GetAsync(id, true, new CancellationToken()).ConfigureAwait(false);
if ((organizationUnit1.ParentId.HasValue==parentId.HasValue ?
(organizationUnit1.ParentId.HasValue ?
(organizationUnit1.ParentId.GetValueOrDefault() == parentId.GetValueOrDefault() ? 1 : 0)
: 1)
: 0)
!= 0)
{
}
else
{
var children = await this.FindChildrenAsync(new Guid?(id), true).ConfigureAwait(false);
var oldCode = organizationUnit1.Code;
await SetCodeAsync(organizationUnit1);
await SetParentIdAsync(organizationUnit1, parentId);
foreach (OrganizationUnit entity in children)
{
await SetCodeAsync(entity,OrganizationUnit.AppendCode(organizationUnit1.Code, OrganizationUnit.GetRelativeCode(entity.Code, oldCode)));
await this.OrganizationUnitRepository.UpdateAsync(entity).ConfigureAwait(false);
}
await this.OrganizationUnitRepository.UpdateAsync(organizationUnit1).ConfigureAwait(false);
children = (List<OrganizationUnit>) null;
oldCode = (string) null;
}
organizationUnit1 = (OrganizationUnit) null;
}
public async Task SetParentIdAsync(OrganizationUnit organizationUnit,Guid? parentId)
{
var pf = GetPropertyInfo("ParentId");
if (pf != null)
{
pf.SetValue(organizationUnit, parentId);
}
}
这里同样加了一个辅助方法SetParentIdAsync。
两个问题
MoveAsync方法里有两点让我比较好奇,一个是if条件判断里写了一个复杂的三项表达式,另一个是方法结尾主动将定义的对象赋值为null了。
首先我们分析一下这个三项表达式在干嘛:
我们先看=0条件下才会进if,否则都是else,也就是要改变父级id。
第一段organizationUnit1.ParentId.HasValue==parentId.HasValue ?():0,这步判断直接就限定了原组织机构父级id和现父级id都为null或者都不为null,否则直接进else了
第二段organizationUnit1.ParentId.HasValue ?():1这步判断看原机构父级id是否有值,如果是null,直接就进if了,如果不是null,看第三步判断
第三段organizationUnit1.ParentId.GetValueOrDefault() == parentId.GetValueOrDefault() ? 1 : 0 第二步判断过了,在第三步中父级id肯定是有值的,如果俩父级id相等,进if,否则进else
总结一下全部进if的情况:
俩父级id都为null;
俩父级id都不为null,但是俩父级id相等;
if条件里我们只需要判断一下俩父级id是不是相等就可以了,我们简化一下MoveAsync代码:
[UnitOfWork]
public override async Task MoveAsync(Guid id, Guid? parentId)
{
OrganizationUnit organizationUnit1 = await this.OrganizationUnitRepository.GetAsync(id, true, new CancellationToken()).ConfigureAwait(false);
if (organizationUnit1.ParentId != parentId)
{
var children = await this.FindChildrenAsync(new Guid?(id), true).ConfigureAwait(false);
var oldCode = organizationUnit1.Code;
OrganizationUnit organizationUnit2 = organizationUnit1;
await SetCodeAsync(organizationUnit2, await this.GetNextChildCodeAsync(parentId).ConfigureAwait(false));
organizationUnit2 = (OrganizationUnit) null;
await SetParentIdAsync(organizationUnit1, parentId);
foreach (OrganizationUnit entity in children)
{
await SetCodeAsync(entity, OrganizationUnit.AppendCode(organizationUnit1.Code, OrganizationUnit.GetRelativeCode(entity.Code, oldCode)));
OrganizationUnit organizationUnit = await this.OrganizationUnitRepository.UpdateAsync(entity).ConfigureAwait(false);
}
OrganizationUnit organizationUnit3 = await this.OrganizationUnitRepository.UpdateAsync(organizationUnit1).ConfigureAwait(false);
children = (List<OrganizationUnit>) null;
oldCode = (string) null;
}
organizationUnit1 = (OrganizationUnit) null;
}
看下第二点:
在C#中,将一个对象赋值为null并不会立即释放该对象的内存。当对象没有任何引用指向它时,它会成为垃圾对象,并在垃圾回收器运行时被回收并释放内存。
需要注意的是,手动将一个对象赋值为null并不是必要的,因为在代码执行到作用域结束时,该对象的引用将会被自动释放,垃圾回收器会负责回收不再使用的对象的内存。因此,在绝大多数情况下,不需要手动将对象赋值为null来释放内存。
此外,过度使用手动赋值为null可能会导致代码可读性和可维护性的下降,因此建议在真正需要时才使用。
在这段代码中,将对象赋值为null后,如果没有其他引用指向该对象,那么该对象将成为垃圾对象,并在垃圾回收器运行时被回收。
那么什么情况下需要我们手动去赋值null呢:
- 对于大型对象或占用大量内存的对象,如果你知道该对象在后续的代码中不再需要使用,可以手动将其赋值为null来提前释放内存,以避免内存占用过高。
- 在长时间运行的方法或循环中,如果你创建了大量临时对象,并且这些对象在后续的代码中不再需要使用,可以手动将它们赋值为null来提前释放内存,以避免内存泄漏和性能问题。
- 在使用大对象或占用大量内存的数据结构时,如果你知道不再需要使用该数据结构,可以手动将其赋值为null来释放内存,以便垃圾回收器可以更早地回收内存。
大型对象指的就是垃圾回收中LOH和POH部分的占用,从abp框架项目内存占用分析来看LOH和POH的占用确实不小,从源码的写法来看,我们猜测一下abp的框架项目运行的时候会有一些不少的大型对象和固定对象占用着内存(这部分以后找机会验证一下)。
这里我们先保留方法里赋值为null的写法。
2.体验效果
我们写两个方法看下重写后的方法的使用效果:
public async Task CreateOrganizationAsync(string organizationName,Guid? parentId)
{
var org = new OrganizationUnit(Guid.NewGuid(), organizationName, parentId);
await _organizationManager.CreateAsync(org);
}
public async Task MoveOrganizationAsync(Guid id,Guid? parentId)
{
await _organizationManager.MoveAsync(id,parentId);
}
调用CreateOrganizationAsync两次:
创建成功了,code值也有。
下面试试MoveOrganizationAsync:
分别试 :
- 父级id null,移动到null;
- 父级id null,移动到非null;
- 父级id 非null,移动到非null,父级id相同;
- 父级id 非null,移动到非null,父级id不同
- 父级id 非null,移动到null;
这里我们打断点看代码执行效果是否和原代码走的逻辑一样。
具体验证过程就不写了,按照这几种情况验证一下即可,本文在本地验证后这几种情况实现效果和代码逻辑走的都没问题。