Uganda.AdministrativeUnits 1.1.1

dotnet add package Uganda.AdministrativeUnits --version 1.1.1
                    
NuGet\Install-Package Uganda.AdministrativeUnits -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Uganda.AdministrativeUnits" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Uganda.AdministrativeUnits" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Uganda.AdministrativeUnits" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Uganda.AdministrativeUnits --version 1.1.1
                    
#r "nuget: Uganda.AdministrativeUnits, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Uganda.AdministrativeUnits@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Uganda.AdministrativeUnits&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Uganda.AdministrativeUnits&version=1.1.1
                    
Install as a Cake Tool

Uganda.AdministrativeUnits

An offline, dependency-free .NET directory of Uganda's verified administrative units: 146 districts, 353 constituencies, 2,198 subcounties / town councils, 10,717 parishes and 71,230 villages, with official codes, hierarchy navigation and name search.

  • Zero dependencies and no network calls. The dataset (~490 KB compressed) is embedded in the assembly.
  • Immutable and thread-safe. Parsed once on first use (~200 ms, ~24 MB), then every lookup is in-memory.
  • Navigable both ways. village.Parish.Subcounty.Constituency.District, or district.Villages.
  • Nationally unique codes. Source codes only repeat between siblings, so every unit also gets a FullCode such as 100-118-01-13-05.
  • Verified. Counts match the totals printed in the source register.

Targets netstandard2.0 (.NET Framework 4.6.1+, .NET Core 2.0+) and net8.0.

Namespace: Uganda.AdministrativeUnits.

Install

dotnet add package Uganda.AdministrativeUnits

On .NET Framework, NuGet resolves the netstandard2.0 asset (requires 4.6.1 or newer).

using Uganda.AdministrativeUnits;

IAdministrativeUnitDirectory directory = AdministrativeUnitDirectory.Default;

Hierarchy

District → Constituency → Subcounty → Parish → Village
Level Type Meaning
1 District District (incl. capital city district)
2 Constituency County or division
3 Subcounty Subcounty, town council, division, or ward-level unit
4 Parish Parish or ward
5 Village Village, cell, or zone

Code is unique only among siblings. FullCode joins ancestor codes with - and is unique nationwide — persist FullCode, not bare Code.


Contracts (public types)

Everything a consumer can reference is listed below. Constructors are internal; you obtain instances from the directory.

AdministrativeLevel

public enum AdministrativeLevel
{
    District = 1,
    Constituency = 2,
    Subcounty = 3,
    Parish = 4,
    Village = 5,
}

DatasetInfo

public sealed record DatasetInfo(
    string Title,
    string Edition,
    DateOnly PublishedOn,
    string SourceNote,
    int CorrectionsApplied);
Property Meaning
Title Source register title
Edition e.g. "July 2022"
PublishedOn Date the source was generated
SourceNote How the data was obtained
CorrectionsApplied Count of documented source-data fixes

DatasetStatistics

public sealed record DatasetStatistics(
    int Districts,
    int Constituencies,
    int Subcounties,
    int Parishes,
    int Villages);

IAdministrativeUnitDirectory

public interface IAdministrativeUnitDirectory
{
    DatasetInfo Dataset { get; }
    DatasetStatistics Statistics { get; }

    IReadOnlyList<District> Districts { get; }           // ordered by code
    IReadOnlyList<Constituency> Constituencies { get; }
    IReadOnlyList<Subcounty> Subcounties { get; }
    IReadOnlyList<Parish> Parishes { get; }
    IReadOnlyList<Village> Villages { get; }

    District? GetDistrict(string code);
    District? GetDistrictByName(string name);
    AdministrativeUnit? GetByFullCode(string fullCode);

    IReadOnlyList<AdministrativeUnit> Search(
        string query,
        AdministrativeLevel? level = null,
        int maxResults = 25);
}
Member Behaviour
GetDistrict By local code (e.g. "100"). Trims input. null if missing. Throws if code is null.
GetDistrictByName Ignores case, diacritics, apostrophe variants, extra whitespace. null if missing. Throws if name is null.
GetByFullCode Any level by nationwide code (e.g. "100-118-01-13-05"). Trims input. null if missing. Throws if fullCode is null.
Search Ranked name search: exact → prefix → word-prefix → substring. Ties: level, then name, then FullCode. Empty/whitespace query → empty list. maxResults must be positive.

AdministrativeUnitDirectory

public sealed class AdministrativeUnitDirectory : IAdministrativeUnitDirectory
{
    public static AdministrativeUnitDirectory Default { get; }

    public static AdministrativeUnitDirectory Load();

    // + all IAdministrativeUnitDirectory members
}
Member Behaviour
Default Shared, lazily loaded singleton. Prefer this.
Load() New independent instance (re-parses embedded JSON). Use only when you need isolation.

AdministrativeUnit (base)

public abstract class AdministrativeUnit : IEquatable<AdministrativeUnit>
{
    public AdministrativeLevel Level { get; }
    public string Code { get; }              // local; sibling-unique only (e.g. "01")
    public string FullCode { get; }          // nationwide unique (e.g. "100-118-01-13-05")
    public string Name { get; }              // as in source (usually upper case)
    public AdministrativeUnit? Parent { get; } // null for District
    public string Breadcrumb { get; }        // e.g. "KALUNGU › KALUNGU WEST COUNTY › …"

    public IEnumerable<AdministrativeUnit> Ancestors(); // nearest parent first

    public bool Equals(AdministrativeUnit? other);      // Level + FullCode
    public override bool Equals(object? obj);
    public override int GetHashCode();
    public override string ToString();                  // returns Name
}

District

public sealed class District : AdministrativeUnit
{
    public IReadOnlyList<Constituency> Constituencies { get; }
    public IEnumerable<Subcounty> Subcounties { get; }  // flattened
    public IEnumerable<Parish> Parishes { get; }        // flattened
    public IEnumerable<Village> Villages { get; }       // flattened
}

Parent is always null. FullCode equals Code.

Constituency

public sealed class Constituency : AdministrativeUnit
{
    public District District { get; }
    public IReadOnlyList<Subcounty> Subcounties { get; }
    public IEnumerable<Parish> Parishes { get; }        // flattened
    public IEnumerable<Village> Villages { get; }       // flattened
}

Subcounty

public sealed class Subcounty : AdministrativeUnit
{
    public Constituency Constituency { get; }
    public District District { get; }
    public IReadOnlyList<Parish> Parishes { get; }
    public IEnumerable<Village> Villages { get; }       // flattened
}

Parish

public sealed class Parish : AdministrativeUnit
{
    public Subcounty Subcounty { get; }
    public Constituency Constituency { get; }
    public District District { get; }
    public IReadOnlyList<Village> Villages { get; }
}

Village

public sealed class Village : AdministrativeUnit
{
    public Parish Parish { get; }
    public Subcounty Subcounty { get; }
    public Constituency Constituency { get; }
    public District District { get; }
}

Use cases

1. Read dataset metadata and counts

DatasetInfo info = directory.Dataset;
Console.WriteLine($"{info.Title} ({info.Edition}, {info.PublishedOn:yyyy-MM-dd})");
Console.WriteLine($"Corrections: {info.CorrectionsApplied}");

DatasetStatistics stats = directory.Statistics;
Console.WriteLine($"{stats.Districts} districts · {stats.Villages} villages");

2. List every unit at a level (national)

foreach (District d in directory.Districts)
    Console.WriteLine($"{d.Code} {d.Name}");

int parishCount = directory.Parishes.Count;

3. Look up a district by code or name

District? byCode = directory.GetDistrict("100");
District? byName = directory.GetDistrictByName("kalungu");
District? same = directory.GetDistrictByName("  KALUNGU  ");

4. Resolve any unit by FullCode (persist & restore)

AdministrativeUnit? unit = directory.GetByFullCode("100-118-01-13-05");
if (unit is Village village)
{
    Console.WriteLine(village.Breadcrumb);
    // KALUNGU › KALUNGU WEST COUNTY › KALUNGU › KITAMBA › KITAMBA
    Console.WriteLine(village.District.Name);
}

5. Search names (ranked)

foreach (AdministrativeUnit hit in directory.Search("kampala", maxResults: 5))
    Console.WriteLine($"[{hit.Level}] {hit.Breadcrumb} ({hit.FullCode})");

foreach (AdministrativeUnit hit in directory.Search("kyamuzizi", AdministrativeLevel.Village))
    Console.WriteLine(hit.Breadcrumb);

IReadOnlyList<AdministrativeUnit> parishes =
    directory.Search("kitamba", AdministrativeLevel.Parish);

6. Walk the hierarchy down

District kalungu = directory.GetDistrict("100")!;

foreach (Constituency county in kalungu.Constituencies)
    foreach (Subcounty subcounty in county.Subcounties)
        foreach (Parish parish in subcounty.Parishes)
            foreach (Village village in parish.Villages)
                Console.WriteLine(village.FullCode);

// Flattened helpers
int villagesInDistrict = kalungu.Villages.Count();
IEnumerable<Parish> parishesInCounty = county.Parishes;
IEnumerable<Village> villagesInSubcounty = subcounty.Villages;

7. Walk the hierarchy up (typed parents)

Village village = (Village)directory.GetByFullCode("100-118-01-13-05")!;

Console.WriteLine(village.Level);        // Village
Console.WriteLine(village.Code);         // 05
Console.WriteLine(village.FullCode);     // 100-118-01-13-05
Console.WriteLine(village.Name);         // KITAMBA
Console.WriteLine(village.Parent!.Name); // KITAMBA (parish)
Console.WriteLine(village.Breadcrumb);
// KALUNGU › KALUNGU WEST COUNTY › KALUNGU › KITAMBA › KITAMBA

Console.WriteLine(village.Parish.Name);
Console.WriteLine(village.Subcounty.Name);
Console.WriteLine(village.Constituency.Name);
Console.WriteLine(village.District.Name);
// or: village.Parish.Subcounty.Constituency.District

8. Enumerate ancestors

Nearest parent first.

foreach (AdministrativeUnit ancestor in village.Ancestors())
    Console.WriteLine($"{ancestor.Level}: {ancestor.Name}");

9. Cascade select (district → village)

Each dropdown lists children of the selection above. Bind lower levels by FullCode. When a parent changes, clear every deeper selection. Persist the deepest unit’s FullCode.

IReadOnlyList<District> districts = directory.Districts;

District? district = directory.GetDistrict(selectedDistrictCode);
IReadOnlyList<Constituency> constituencies =
    district?.Constituencies ?? Array.Empty<Constituency>();

Constituency? constituency = constituencies
    .FirstOrDefault(c => c.FullCode == selectedConstituencyFullCode);
IReadOnlyList<Subcounty> subcounties =
    constituency?.Subcounties ?? Array.Empty<Subcounty>();

Subcounty? subcounty = subcounties
    .FirstOrDefault(s => s.FullCode == selectedSubcountyFullCode);
IReadOnlyList<Parish> parishes =
    subcounty?.Parishes ?? Array.Empty<Parish>();

Parish? parish = parishes
    .FirstOrDefault(p => p.FullCode == selectedParishFullCode);
IReadOnlyList<Village> villages =
    parish?.Villages ?? Array.Empty<Village>();

Village? village = villages
    .FirstOrDefault(v => v.FullCode == selectedVillageFullCode);

string? persist = village?.FullCode;

Restore a saved selection:

if (directory.GetByFullCode(storedFullCode) is Village v)
{
    string districtCode = v.District.Code;
    string constituencyFullCode = v.Constituency.FullCode;
    string subcountyFullCode = v.Subcounty.FullCode;
    string parishFullCode = v.Parish.FullCode;
    string villageFullCode = v.FullCode;
}

You can stop at any level; still persist that level’s FullCode.

10. Validate a submitted address code

AdministrativeUnit? unit = directory.GetByFullCode(request.FullCode);
if (unit is null)
    throw new ValidationException("Unknown administrative unit.");

if (unit.Level != AdministrativeLevel.Village)
    throw new ValidationException("Expected a village FullCode.");

11. Project to your own DTOs (do not serialize units)

The object graph has parent links (cycles). Map to flat models for APIs and persistence.

public sealed record AddressDto(
    string FullCode,
    string Name,
    string Breadcrumb,
    AdministrativeLevel Level);

AddressDto dto = new(unit.FullCode, unit.Name, unit.Breadcrumb, unit.Level);
AdministrativeUnit? restored = directory.GetByFullCode(dto.FullCode);

12. Compare units

Equality is by Level + FullCode (ordinal).

bool same = unitA.Equals(unitB);
string label = unitA.ToString(); // Name

13. Isolated directory instance

AdministrativeUnitDirectory isolated = AdministrativeUnitDirectory.Load();
// independent parse of the embedded dataset; prefer Default in apps

Design notes

Topic Decision
Hierarchy Same nesting as the source register.
Names Kept verbatim (usually upper case). Lookups/search fold case, Latin diacritics, apostrophe variants, whitespace.
Codes Code = local. FullCode = ancestor path with -; unique nationwide.
Subcounty Source “SUBCOUNTY/TOWN” → type Subcounty.
Search cost O(n) scan with a bounded top-K heap.
Thread safety After load, immutable and safe to share across threads.
Serialization Do not serialize units; project to DTOs and restore via GetByFullCode.

Data and licence

Data is from Uganda's Verified Administrative Units, July 2022 (generated 19 July 2022). This package is a snapshot of that edition, not a live registry.

The MIT licence covers this package's code. The underlying data belongs to its publisher; confirm you may redistribute it before publishing derivatives publicly.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.1 101 10/4/2026
1.1.0 101 10/3/2026
1.0.0 98 10/2/2026

1.1.1: Updated package README. No API changes.