Rewrite the abp framework OrganizationUnitManager class

Background: In the previous article, we rewrote the UserManager class. In this article, we start with the OrganizationUnitManager. The reason is, of course, that there are some unnecessary verifications in the encapsulated method. Of course, the organization name is almost never repeated, but It restricts us from repeating, so I am very unhappy with it (joking). The main purpose here is to remove unnecessary verification and add some auxiliary methods to facilitate us to better use the abp framework.
1. Create the OrganizationManager class in the Domain layer
Here we inherit OrganizationUnitManager (there is no corresponding generic class here). Also because we do not need to rewrite all methods, we start with methods with verification.
Create an organization

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

From the perspective of encapsulation code, the OrganizationUnitManager class is much simpler than the UserManager.
Here we have added an auxiliary method SetCodeAsync, because the Code field set is internal. The reason why reflection is used to assign the value here is because in
There is no method to assign value to Code in the OrganizationUnitManager and OrganizationUnit classes. In the source code, assignment is only called internally when the unit is created and moved. However, we have no choice but to rewrite it. Currently, we can only choose this method to assign value.
At the same time, set SetCodeAsync to public to facilitate future calls.

Update organization
We did not choose to rewrite here because the verification step is removed. In fact, OrganizationUnitRepository.UpdateAsync is called in the source code, so there is no need to rewrite.

Mobile agency:

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

An auxiliary method SetParentIdAsync is also added here.

two questions
There are two points in the MoveAsync method that make me curious. One is that a complex three-term expression is written in the if conditional judgment, and the other is that the defined object is actively assigned to null at the end of the method.

First, let’s analyze what this three-term expression is doing:

We first look at the =0 condition before entering if, otherwise it will be else, which means changing the parent id.
The first paragraph organizationUnit1.ParentId.HasValue==parentId.HasValue? (): 0, this step of judgment directly limits the original organization parent id and the current parent id to be null or neither is null, otherwise directly proceed to else Got it
The second paragraph organizationUnit1.ParentId.HasValue? (): 1 This step determines whether the original organization parent id has a value. If it is null, go to if directly. If it is not null, see the third step for judgment.
The third paragraph organizationUnit1.ParentId.GetValueOrDefault() == parentId.GetValueOrDefault() ? 1 : 0 The second step has been judged. In the third step, the parent ID must have a value. If the two parent IDs are equal, proceed if, otherwise go to else

Summarize all the situations of entering if:
Both parent ids are null;
Neither parent ID is null, but the two parent IDs are equal;

In the if condition, we only need to determine whether the two parent IDs are equal. Let’s simplify the MoveAsync code:

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

Let’s look at the second point:

In C#, assigning an object to null does not immediately release the object’s memory. When an object does not have any references pointing to it, it becomes a garbage object and is recycled and the memory is released when the garbage collector runs.
It should be noted that it is not necessary to manually assign an object to null, because when the code is executed to the end of the scope, the reference to the object will be automatically released, and the garbage collector will be responsible for reclaiming the memory of the object that is no longer used. . Therefore, in the vast majority of cases, there is no need to manually assign the object to null to release memory.
In addition, excessive use of manual assignment to null may lead to a decrease in code readability and maintainability, so it is recommended to only use it when really needed.

In this code, after assigning the object to null, if no other reference points to the object, the object will become a garbage object and be recycled when the garbage collector runs.

So under what circumstances do we need to manually assign null:

  • For large objects or objects that occupy a lot of memory, if you know that the object will no longer be used in subsequent code, you can manually assign it to null to release the memory in advance to avoid excessive memory usage.
  • In a long-running method or loop, if you create a large number of temporary objects and these objects are no longer needed in subsequent code, you can manually assign them to null to release the memory in advance to avoid memory leaks and performance problems. .
  • When using large objects or data structures that take up a lot of memory, if you know that you no longer need to use the data structure, you can manually assign it to null to release the memory so that the garbage collector can reclaim the memory earlier.

Large objects refer to the occupation of LOH and POH parts in garbage collection. From the analysis of the memory occupation of the abp framework project, it can be seen that the occupation of LOH and POH is indeed not small. Judging from the writing of the source code, we can guess when the abp framework project is running. There will be a lot of large objects and fixed objects occupying memory (I will find an opportunity to verify this part later).

Here we first retain the writing method of assigning null to the method.

2.Experience the effect
Let’s write two methods to see the effect of the rewritten method:

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

Call CreateOrganizationAsync twice:

The creation was successful and the code value is also there.

Try MoveOrganizationAsync below:
Try separately:

  • Parent id is null, move to null;
  • Parent id is null, moved to non-null;
  • The parent id is non-null, move to non-null, and the parent id is the same;
  • The parent id is non-null, move to non-null, the parent id is different
  • If the parent id is not null, move to null;
    Here we break the point to see whether the code execution effect is the same as the logic of the original code.
    I won’t write down the specific verification process. Just verify it according to these situations. After local verification in this article, the implementation effects and code logic of these situations are no problem.