Files
2026-03-09 08:54:42 +07:00

6.1 KiB

PROJECT KNOWLEDGE BASE

Generated: 2026-02-09 Commit: 107722278e9274073236e8ee4cba21f832b2ed86 Branch: main

OVERVIEW

.NET 10.0 console application for extracting employee tax data from PostgreSQL multi-schema databases. Uses static service classes with synchronous operations.

PROJECT STRUCTURE

./
├── excel_pajak/              # Main application
│   ├── Models/               # Data models (EmployeeInfo)
│   ├── Services/             # Static services (DatabaseService, ConfigurationService)
│   ├── Examples/             # Code examples
│   ├── appsettings.json      # Configuration
│   └── excel_pajak.csproj    # Main project
├── excel_pajak_test/         # MSTest project
└── openspec/                 # OpenSpec change management artifacts

COMMANDS

Build

# Build entire solution
dotnet build

# Build specific project
dotnet build excel_pajak
dotnet build excel_pajak_test

Run Tests

# Run all tests
dotnet test

# Run single test class
dotnet test --filter "FullyQualifiedName~DatabaseServiceTests"

# Run single test method
dotnet test --filter "FullyQualifiedName~DatabaseServiceTests.ExecuteScalar_NullConnectionString_ThrowsArgumentException"

# Run tests with specific logger
dotnet test --logger "console;verbosity=detailed"

# Run tests without building
dotnet test --no-build

Run Application

# Run with default project
dotnet run --project excel_pajak

# Run with arguments
dotnet run --project excel_pajak -- --schema=company_2024 --year=2024

Clean and Rebuild

# Clean solution
dotnet clean

# Clean and rebuild
dotnet clean && dotnet build -c Release

CODE STYLE GUIDELINES

General Principles

  • Target .NET 10.0 with latest C# features
  • Enable ImplicitUsings and Nullable reference types
  • No comments unless required for complex logic

Imports

  • Use file-scoped namespaces (namespace excel_pajak.Services;)
  • Group imports: System namespaces first, then third-party
  • Order: System.*Microsoft.*ThirdParty.*Project.*
using Microsoft.Extensions.Configuration;
using Npgsql;

namespace excel_pajak.Services;

Naming Conventions

  • Classes: PascalCase (DatabaseService, EmployeeInfo)
  • Methods: PascalCase (ExecuteScalar, Initialize)
  • Properties: PascalCase (EmployeeNumber, ConnectionString)
  • Local variables: camelCase (connectionString, result)
  • Constants: UPPER_SNAKE_CASE for values, PascalCase for statics
  • Private fields: camelCase with underscore prefix (_connectionString)
  • Parameters: camelCase (schema, year)

Types

  • Use nullable reference types (string?, int?)
  • Prefer var when type is obvious from context
  • Use explicit types for public APIs and method returns
  • Use string? instead of string with null annotations for nullable properties

Error Handling

  • Validate arguments with ArgumentException for null/empty checks
  • Wrap database exceptions with InvalidOperationException
  • Use result tuples for recoverable errors: (bool success, string? result, Exception? error)
  • Re-throw exceptions with context using throw new ExceptionType("message", inner)
if (string.IsNullOrWhiteSpace(connectionString))
    throw new ArgumentException("Connection string cannot be null or empty.", nameof(connectionString));

try { /* ... */ }
catch (NpgsqlException ex)
{
    throw new InvalidOperationException($"Database error: {ex.Message}", ex);
}

Static Service Pattern

  • Services use static classes with Initialize() pattern
  • Lazy initialization for dependencies
  • No async database operations (intentional - see ANTI-PATTERNS)
public static class DatabaseService
{
    private static string? _connectionString;

    public static void Initialize(string connectionString)
    {
        _connectionString = connectionString;
    }
}

JSON Serialization

  • Use System.Text.Json with [JsonPropertyName] attributes
  • Snake_case for JSON property names
public class EmployeeInfo
{
    [JsonPropertyName("employee_number")]
    public string? EmployeeNumber { get; set; }
}

Configuration

  • Use appsettings.json for all configuration
  • Access via Microsoft.Extensions.Configuration
  • Bind options with ConfigurationBinder.Bind()

Validation

  • Schema names: ^[a-zA-Z0-9_-]+$
  • Years: ^\d{4}$
private static readonly Regex SchemaValidation = new(@"^[a-zA-Z0-9_-]+$", RegexOptions.Compiled);

Database Operations

  • Synchronous only using Npgsql
  • Use json_agg(row_to_json(t)) for PostgreSQL JSON aggregation
  • Placeholder replacement: query.Replace("{schema}", schema)
  • Dispose resources with using statements or using var

Testing

  • Use MSTest with [TestClass] and [TestMethod]
  • Use Shouldly for assertions: Should.Throw<>, ShouldBe(), ShouldBeNull()
  • Use Moq for mocking
  • Mark integration tests with [Ignore] if they require database connection
  • Use region blocks (#region) for grouping related tests

WHERE TO LOOK

Task Location
Database operations excel_pajak/Services/DatabaseService.cs
Configuration handling excel_pajak/Services/ConfigurationService.cs
Data models excel_pajak/Models/EmployeeInfo.cs
Tests excel_pajak_test/DatabaseServiceTests.cs
Configuration excel_pajak/appsettings.json

ANTI-PATTERNS TO AVOID

  • No async database operations (intentional design, may need future migration)
  • No connection pooling or disposal tracking in services
  • Static state requires explicit Initialize() before use

KEY DEPENDENCIES

  • Npgsql: PostgreSQL driver (sync operations)
  • NPOI: Excel file manipulation
  • Microsoft.Extensions.Configuration: Configuration management
  • Shouldly: Test assertions
  • Moq: Mocking framework
  • MSTest: Test framework

NOTES

  • All tests use MSTest framework
  • Configuration requires valid connection string in appsettings.json
  • Schema and Year parameters drive query execution
  • Connection strings validated with regex for security