The ADUserCache class that we wrote previously seems to be a pretty good base to start from, let's see what else we can do with it.

### Extending our cache class

Another useful behavior we might want to implement could be the ability for pre-seed the cache. I mean, we already have the $Agents list of users in memory, and populating a hash table is not terribly expensive (and we only need to do it once!). As previously mentioned, PowerShell classes allow us to overload methods and constructors just like in C#. That is, provide multiple implementations of the same method with different signatures - the distinct combination of return type, parameter types and method name. #### Overloading constructors! Let's overload the constructor: class ADUserCache # ... ADUserCache([psobject[]]$InitialUsers){
$this.LookupTable = @{} foreach($User in $InitialUsers){$this.LookupTable[$User.DistinguishedName] =$User
}
}
# ...
}


Awesome, now we can re-use the set of user account objects that we've already collected:

# Save all users to a variable
$Agents = @(Get-ADUser -Filter "title -like '*agent*'" -Properties manager) # Start out with a pre-seeded lookup table$UserCache = [ADUserCache]::new($Agents) foreach($Agent in $Agents){ [pscustomobject]@{ Agent =$Agent.Name
Manager = $UserCache.GetADUser($Agent.manager).Name
}
}


Another feature that might come in handy is the ability to clear our cache, and maybe selectively evict individual items.

The [hashtable] type already has a method for this: Clear() - we'll reuse the method signature (more about why later), and implement a simple wrapper method in our ADUserCache class:

class ADUserCache
# ...
[void] Clear(){
$this.LookupTable.Clear() } # ... }  #### Remove() Next up is the ability to clear individual items from our cache. Once again, [hashtable] already exposes this functionality with the Remove() method - once again we just wrap it in a method of the same name, this time passing a string argument to identify the relevant item in the cache: class ADUserCache # ... [void] Remove([string]$Identity){
$this.LookupTable.Remove($Identity)
}
# ...
}


With this in place, we can allow the user to clear just a single entry without starting completely over from scratch.

#### Refresh()

Lastly, we might want a "refresh" method - a method that acts like GetADUser(), but forces a refresh of the cache entry for that item regardless of whether it's already present or not.

This time we'll have to come up with the logic ourselves, as [hashtable] doesn't already have something like this (for the good reason that it's just a hashtable - not a specialized cache type).

We can reuse the functions we already have though, so no worries:

class ADUserCache
# ...
[psobject] Refresh([string]$Identity){$this.Remove($Identity) return$this.GetADUser($Identity) } # ... }  Another common pattern one might find in a cache implementation like ours is an optional boolean that forces the cache to go out and fetch a fresh copy of our data, like so: class ADUserCache # ... [psobject] GetADUser([string]$Identity, [bool]$ForceRefresh){ if((-not$ForceRefresh) -and $this.LookupTable.Contains($Identity)){
return $this.LookupTable[$Identity]
}
else{
return ($this.LookupTable[$Identity] = Get-ADUser -Identity $Identity -ErrorAction SilentlyContinue) } } # ... }  You'll notice that this code is almost identical to the original implementation of GetADUser() - so let's re-write its call chain t reuse this new overload (did I mention that I hate repeating code?): class ADUserCache # ... [psobject] GetADUser([string]$Identity){
return $this.GetADUser($Identity, $false) } [psobject] GetADUser([string]$Identity, [bool]$ForceRefresh){ if((-not$ForceRefresh) -and $this.LookupTable.Contains($Identity)){
return $this.LookupTable[$Identity]
}
else{
return ($this.LookupTable[$Identity] = Get-ADUser -Identity $Identity -ErrorAction SilentlyContinue) } } # ... }  I personally don't like this pattern, so I'll be sticking with a separate Refresh() method. This is obviously highly subjective, so feel free to pursue whatever strategy works for you! ### Final implementation class ADUserCache { [hashtable]$LookupTable

$this.LookupTable = @{} } ADUserCache([psobject[]]$InitialUsers){
$this.LookupTable = @{} foreach($User in $InitialUsers){$this.LookupTable[$User.DistinguishedName] =$User
}
}

[void] Clear(){
$this.LookupTable.Clear() } [void] Remove([string]$Identity){
$this.LookupTable.Remove($Identity)
}

[psobject] Refresh([string]$Identity){$this.Remove($Identity) return$this.GetADUser($Identity) } [psobject] GetADUser([string]$Identity){
if($this.LookupTable.Contains($Identity)){
return $this.LookupTable[$Identity]
}
else{
return ($this.LookupTable[$Identity] = Get-ADUser -Identity \$Identity -ErrorAction SilentlyContinue)
}
}
}


### Conclusion

The ADUserCache class is starting to look, act and feel like a real caching facility, how about that!

However, as I mentioned in the last post in this series, I have a huge pet peeve with the direction I've taken here:

Wouldn't it be nice if we could reuse our glorious cache class without copy-pasting and modifying the ADUserCache definition?

Next up, we'll look into generalizing our cache implementation - stay tuned!