Skip to content

About

Maintenance and tooling for DiGi solutions - solution configuration, directory cleanup, and custom Roslyn analyzers/code fixes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

Β 

History

327 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

DiGi.Maintenance

DiGi.Maintenance is a C# engineering and architectural software library suite designed for BIM and CAD integrations (such as Revit, RhinoCommon, Grasshopper, and Dynamo BIM).


πŸ—οΈ Project Architecture & Assemblies

The repository contains the following core components and assemblies:


πŸ“ Core Architectural Pattern (DiGi.Core Pattern)

This project strictly separates Data Models (anemic schemas) from Business/Calculation Logic (static extension methods). All new features must strictly follow this pattern.

graph TD
    subgraph "Data Models (Anemic)"
        C[Classes]
        I[Interfaces]
        E[Enums]
    end
    subgraph "Business Logic (Static Extensions)"
        Q[Query Class]
        M[Modify Class]
        Cr[Create Class]
    end
    C -->|Query / Read| Q
    C -->|Modify / Mutate| M
    C -->|Create / Instantiate| Cr
Loading

1. Data Models (Classes, Interfaces, Enums)

  • Classes: Place in the /Classes directory (Namespace: [Project].Classes). Keep them simple and lightweight (properties and basic constructors only). Do NOT put complex logic inside these classes.
  • Interfaces: Place in the /Interfaces directory (Namespace: [Project].Interfaces).
  • Enums: Place in the /Enums directory (Namespace: [Project].Enums).

2. Business Logic (Extension Methods)

ALL complex functionalities, including operations on classes, interfaces, and enums, MUST be implemented as Extension Methods inside static partial classes in /Query, /Modify, /Create, or /Convert directories:

  • Query (Read/Extract): Static partial class Query returning results based on a query without modifying the source object.
  • Modify (Update/Mutate): Static partial class Modify modifying the state or properties of the existing object in place.
  • Create (Instantiate): Static partial class Create instantiating and returning a new object.
  • Convert (Parse/Format/Transform): Static partial class Convert converting, formatting, or transforming an object into another representation.

πŸ’» Coding Guidelines for Developers & AI Agents

All developers and AI agents follow these always-applicable rules (Visual Studio 2026, C#). Task-specific detail lives in the guidelines listed in Β§9 β€” read the one matching the task.

1. General coding standards

  1. English only β€” identifiers and comments.
  2. No var unless the compiler forces it; target-typed new() (PointNode pointNode = new();, avoids IDE0090) and collection expressions (List<int> numbers = [];, avoids IDE0028).
  3. Naming: camelCase starting with the type name, _-suffixed qualifier when needed (PointNode pointNode_Base); an acronym type lowercases only its first letter (gISPostgreSQLConverterManager). Collections: pluralized element type (filterConditions, point3Ds β€” not listConditions). A property named after its unique, descriptive type (public AggregateFunction AggregateFunction { get; set; }). Primitives: plain camelCase.
  4. Zero warnings/analyzer messages.
  5. Block-scoped namespaces (namespace DiGi.Domain { ... }); file-scoped are prohibited (.editorconfig). Every repository builds at LangVersion latest with AnalysisLevel latest-recommended (template props); core libraries target netstandard2.0 β€” use System.Threading.Lock only in net9+ projects.
  6. Parameter line breaks (<= 7 rule, DIGI0001): ≀ 7 parameters on one line however long; β‰₯ 8 one per line. Line length is never the trigger. Call sites prefer one line; multi-line for complex expressions, lambdas, initializers.
    public void Calculate(double centerX, double centerY, double radius, double? storeyHeight = null)
  7. Async method names end with Async.
  8. CancellationToken is always the LAST parameter (CA1068) β€” every method and overload; add new optional parameters before it and reorder the <param> tags. At call sites name every argument after a skipped default (commandTimeout: 30, cancellationToken: cancellationToken) so an inserted parameter fails to compile instead of rebinding. Detect: grep -rnE "CancellationToken [a-zA-Z_]+( = default)?, " --include=*.cs .
    public static async Task<bool> ClearAsync(NpgsqlConnection? npgsqlConnection, string tableName, int commandTimeout = 30, CancellationToken cancellationToken = default)
  9. Simplify member access (IDE0002) β€” then rebuild. C# binds the first segment innermost-outwards with no fallback: from DiGi.GIS.PostgreSQL.UI.Classes, WebAPI is DiGi.GIS.WebAPI, so DiGi.WebAPI.Query.GetAsync keeps its prefix. When same-named extension methods can both apply, call the intended one fully qualified β€” the using decides silently.
  10. Separate projects, not one monolithic solution.
  11. Output optimization: no filler; only the necessary code, logic or explanation.
  12. Temporary code carries TODO [MarkerName]: β€” a PascalCase name of the migration, reused at every site so one grep is the deletion checklist, stating the observable removal condition and the tracking issue. A workaround for another DiGi repository's defect is temporary too (removal condition = the upstream fix). Never tag permanent code.
  13. ToString() is not a key β€” identity strings render through CultureInfo.InvariantCulture ("R" with -0.0 folded, G29 for decimal); pl-PL files [0,5, 12,25] and nothing throws. Reference: DiGi.Typology.Visual.Query.Key.
  14. A member resolved by name through reflection is a contract on the whole hierarchy β€” document it at both ends, probe GetMethod rather than reason about it.
  15. No caching in an optimization unless the request explicitly allows it β€” remove allocations and repeated enumeration first; propose a cache with its measurement and ask.
  16. Log through Serilog.Modify.Log with structured templates ("{Type}: … {CountyId}", never interpolation); Log(exception, …) always logs at Error.
  17. Rethrow only a requested cancellation: catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { throw; } before any general catch.

2. Architecture (DiGi.Core pattern)

Data models are anemic; behavior is static extension methods. Never a service/manager class for behavior (process-wide registries are *Managers on Settings or DI singletons).

  • /Classes ([Project].Classes), /Interfaces, /Enums. Category folders (Classes/Options, /BackgroundTask, /Result, Convert/ToX) add no namespace segment β€” it would change the _type discriminator.
  • Query (read-only, property-like names: BoundingBox(), only Is/Has/Try prefixes), Modify (mutates), Create (builds), Convert (/Convert/To[TargetArea]/[TargetType].cs, ToSystem_String, ToEPW_DateTime) β€” Convert is the first choice for any conversion, never an instance method on the model.
  • Interface-contract members are instance methods on the class β€” geometry primitives (Polygon2D, Circle2D, … implementing IBoundable2D, ITransformable2D, IClosedCurve2D) are deliberately not anemic; never move them.
  • Constructors stay cheap (assign and clone only); validation and cleanup go in a Create factory named after the type, returning null for invalid input β€” materialise β†’ clean up β†’ then validate.
  • One public method per file in /Query, /Modify, /Create (overloads together); nested types in [Outer].[Inner].cs; no private methods in these classes (public helper or local function).
  • IResult types end in Result (TerrainPointDensityResult); the name is wire contract, so existing violations are not renamed opportunistically.
  • Enums start with Undefined (-1 only where 0 is real); tolerances come from DiGi.Core.Constants.Tolerance (Distance 1e-6, MicroDistance, MacroDistance, Angle).

3. Project assets β€” files/ vs user files/ (never commit secrets)

Committed, non-sensitive deployment assets go in files/; secrets, credentials, machine-specific settings and generated reports go in the git-ignored user files/ ([Uu]ser [Ff]iles/ in .gitignore; verify with git check-ignore -v "user files/x.conf"). Both are copied flat into the output (CopyUserFiles runs after CopyFiles, so user files/ wins) β€” and user files/ ships with every deploy, so training data and model variants live outside the workspace. A file a build copies into a project (gltf-viewer-core.js, gis-theme.css) is edited only at its source.

4. Host dependencies β€” HintPath drops a library's NuGet packages

DiGi projects reference each other by HintPath, which NuGet and MSBuild cannot follow: a host must re-declare every package its libraries need, at the same version (unless its framework provides it), or it fails at run time with a FileNotFoundException per item β€” a partial result reported as success. dotnet test never rebuilds the library under test: build it first. Verify with CheckHostDependencies.ps1.

5. References (IReference / IUniqueReference)

Never compare two interface-typed references with == (a silent reference comparison; accessors clone per call). Use Core.Query.Equals(reference_1, reference_2).

6. Serialization (SerializableObject)

A serializable class implements I<Project>SerializableObject, keeps private readonly fields tagged [JsonInclude, JsonPropertyName(nameof(Prop))], exposes them as [JsonIgnore] get-only properties, uses DateTimeOffset for dates, and has exactly three constructors:

public Holiday(string? name, string? date)
{
    this.name = name;
    this.date = date;
}

public Holiday(Holiday? holiday) : base(holiday)
{
    if (holiday != null)
    {
        name = holiday.name;
        date = holiday.date;
    }
}

public Holiday(JsonObject? jsonObject) : base(jsonObject) { }

Copy constructor: primitive lists new List<T>(source), nested objects Core.Query.Clone(item) element by element.

7. Documentation and tests

  • XML docs: every public member, params in signature order, one <summary>, no blank lines (<para>), no docs on partial declarations. Read documentation/API/<Assembly>/ before opening .cs source.
  • xUnit: [ProjectName].xUnit, public partial class Facts in /Facts, a <summary> on every fact, shared fixtures in DiGi.Test/files/, written reports in DiGi.Test/user files/reports/. Every serializable class gets a fact ending in Core.xUnit.Query.SerializationCheck(instance) plus a direct copy-constructor assertion. Tolerance facts test just inside and just outside; benchmarks are measured in isolation with a warm-up. Slow facts are [MediumFact]/[LongFact].
  • Interactive front ends are verified in a real browser (Playwright), asserting rendered output, not state flags.

8. Branches and releases

Work is committed on the active version branch (the highest bare SemVer branch, e.g. 0.8.5) β€” no feature branch, worktree or pull request unless asked; push only when asked. A version bump / release runs only on an explicit request: merge the version branch into main, bump the patch (0.8.2 β†’ 0.8.3), branch it off main, update Directory.Build.props <Major>/<Minor>/<Build>, push both (git push -u origin <version_branch>).

9. Full guideline set

Canonical, task-specific guidelines live in DiGi.Maintenance/documentation/AI Guidelines/ (relative to the workspace root) and are mirrored per repository as skills under .agents/skills/<skill-name>/SKILL.md.

Guideline Read it when
Coding - General.md Writing or editing any C# code.
Coding - Serialization.md Creating or changing a serializable type or options class; debugging a JSON round trip.
Coding - Host Dependencies.md Adding a NuGet package; a host failing with FileNotFoundException or a partial result; CS0012/CS0246 build walls.
Coding - Project Setup.md New project or repository, .editorconfig, Directory.Build.props, analyzers.
Coding - Automatic Tests.md Writing xUnit facts.
Coding - Performance.md Optimizing, benchmarks, parity tests, the wiki Benchmark.md page.
Coding - References.md Comparing or keying an IReference / IUniqueReference.
Coding - Geometry.md Orienting, closing, triangulating, hashing or sweeping geometry.
Coding - ComputeSharp.md Writing or changing a ComputeSharp IComputeShader.
Coding - Grasshopper.md Grasshopper/Rhino plug-in components.
Coding - Background Tasks.md Tray-application background tasks and their dialogs.
Coding - WPF Deferred Loading.md A WPF window needing slow data; WPF layout checks.
Coding - YOLO.md The YOLO Year Built detector.
Coding - Laya.md The Laya decision model.
Coding - WebAPI GLTF.md Web APIs on the DiGi.GLTF 3D framework.
Coding - WebAPI Contracts.md Changing a WebAPI route/parameter/controller or writing an HTTP client.
Coding - WebAPI Simple Authorization.md API-key tiered authorization.
Coding - Deployed WebAPI.md Testing against api.digiproject.uk; which server runs what.
Coding - Browser Testing.md Verifying interactive front-end behaviour in a real browser.
Coding - GIS Administrative Data.md Anything keyed by a county code or id; the Subdivision layer.
Coding - PostgreSQL.md PostgreSQL/Npgsql code, production vs development databases.
Coding - PostgreSQL Distributed Queue Processing.md Distributed claim/acknowledge update queues.
XML Documentation.md Reading generated API docs; adding or auditing XML docs.
GitHub - Branches and Commits.md Where to commit, the pre-commit sweep, plan files, pulling all repos, releases.
GitHub - Issues.md Creating, editing, verifying and closing issues; dependencies.
GitHub - Labels.md Labels and the mandatory ai: * tier.
GitHub - Sub-Issues.md Sub-issues for a multi-repository feature.
GitHub - Workspace Setup.md Folder layout and new-machine setup.
GitHub Wiki.md Editing wiki pages.
Maintenance - Scripts.md DiGi.Maintenance/Scripts; scripted edits and line endings.
Maintenance - AI Guidelines Propagation.md Adding, splitting or removing a guideline.

One data rule worth knowing before it bites you: a GIS county code does not identify one row β€” 406 county rows (one per polygon part) cover 380 codes. Key every write and lookup by county id, give every LIMIT/FirstOrDefault an ORDER BY, never deduplicate those rows, resolve every item's part against building_2d whatever number of ids arrived, and read building-keyed tables with fallbackByReference: true (Coding - GIS Administrative Data.md).

About

Maintenance and tooling for DiGi solutions - solution configuration, directory cleanup, and custom Roslyn analyzers/code fixes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages