Vollständige Implementierung des EngineeringSync-Middleware-Tools: - Windows Service (Kestrel :5050) mit FileSystemWatcher + SignalR - WPF Tray-App mit PendingChanges- und Projektverwaltungs-Fenster - Setup-Wizard (8-Schritte-Installer) - SQLite/EF Core Datenschicht (WAL-Modus) - SHA-256-basiertes Debouncing (2s Fenster) - Backup-System mit konfigurierbarer Aufbewahrung Bugfixes & Verbesserungen: - BUG-1: AppDbContext OnConfiguring invertierte Bedingung behoben - BUG-2: Event-Handler-Leak in TrayApp (Fenster-Singleton-Pattern) - BUG-3: ProjectConfigChanged SignalR-Signal in allen CRUD-Endpoints - BUG-5: Rename-Sync löscht alte Datei im Simulations-Ordner - BUG-6: Doppeltes Dispose von SignalR verhindert - BUG-7: Registry-Deinstallation nur EngineeringSync-Eintrag entfernt - S1: Path-Traversal-Schutz via SafeCombine() im SyncManager - E1: FSW Buffer 64KB + automatischer Re-Scan bei Overflow - E2: Retry-Logik (3x) für gesperrte Dateien mit exponentiellem Backoff - E4: Channel.Writer.TryComplete() beim Shutdown - C2: HubMethodNames-Konstanten statt Magic Strings - E3: Pagination in Changes-API (page/pageSize Query-Parameter) - A1: Fire-and-Forget mit try/catch + Logging Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.3 KiB
6.3 KiB
AGENTS.md
Build, Test & Run Commands
# Build entire solution
dotnet build EngineeringSync.slnx
# Run the background service (dev mode)
dotnet run --project EngineeringSync.Service
# Run the WPF tray app
dotnet run --project EngineeringSync.TrayApp
# Run all tests
dotnet test
# Run a single test project
dotnet test EngineeringSync.Tests --filter "FullyQualifiedName~WatcherServiceTests"
# Run tests with verbose output
dotnet test --verbosity normal
# Build Release
dotnet build EngineeringSync.slnx -c Release
# Install as Windows Service (production)
sc create EngineeringSync binPath="<path>\EngineeringSync.Service.exe"
# Build installer (requires Inno Setup 6)
.\installer\build-installer.ps1
Code Style Guidelines
General Principles
- Use C# 12+ features (file-scoped namespaces, collection expressions, primary constructors)
- Use
[ObservableProperty]and[RelayCommand]attributes from CommunityToolkit.Mvvm - Prefer source generators over boilerplate code
Imports
- Sort by: System namespaces first, then Microsoft, then third-party, then project-specific
- Use file-scoped namespaces:
namespace EngineeringSync.Domain.Entities; - Global using statements in each project for common types
Types & Collections
- Use collection expressions
[]instead ofnew List<T>()where possible - Use
IReadOnlyList<T>for parameters that should not be modified - Use
Guidfor all ID types - Use
DateTime.UtcNowfor timestamps, convert to local time in UI layer only
Naming Conventions
- Classes/Interfaces: PascalCase (e.g.,
WatcherService,IApiClient) - Methods: PascalCase (e.g.,
StartWatchingAsync) - Private fields: camelCase (e.g.,
_watchers,_channel) - Properties: PascalCase (e.g.,
IsActive,EngineeringPath) - Parameters: camelCase (e.g.,
project,stoppingToken) - Constants: PascalCase (e.g.,
DefaultTimeout)
Records vs Classes
- Use
recordfor DTOs and API models - Use
classfor entities with mutable properties
Error Handling
- Use
try/catchwith specific exception types - Log exceptions with context using
ILogger(use structured logging:logger.LogError(ex, "Message {Param}", param)) - In ViewModels, catch exceptions and set
StatusMessagefor user feedback
Async/Await
- Always use
Asyncsuffix for async methods - Pass
CancellationTokento all cancellable operations - Use
await usingforIDisposableresources that implementIAsyncDisposable
Entity Framework Core
- Use
IDbContextFactory<AppDbContext>for scoped DbContext in background services - Use
await using var db = await dbFactory.CreateDbContextAsync(ct); - Configure SQLite with WAL mode for better concurrency
WPF / MVVM
- ViewModels inherit from
ObservableObject(CommunityToolkit.Mvvm) - Use
[ObservableProperty]for properties that need change notification - Use
[RelayCommand]for commands - Use
partial classfor generated properties
Dependency Injection
- Constructor injection with primary constructors:
public sealed class WatcherService( IDbContextFactory<AppDbContext> dbFactory, IHubContext<NotificationHub> hub, ILogger<WatcherService> logger) : BackgroundService
File Organization
EngineeringSync.Domain/ - Entities, Enums, Interfaces
EngineeringSync.Infrastructure/ - EF Core, DbContext, Migrations
EngineeringSync.Service/ - API, Hubs, Background Services
EngineeringSync.TrayApp/ - WPF Views, ViewModels, Services
EngineeringSync.Setup/ - Installer wizard
Key Patterns
FileSystemWatcher Debouncing: Events flow into a Channel<FileEvent>, consumer groups by (ProjectId, RelativePath) within 2000ms window, SHA-256 confirms actual changes.
SignalR Notifications: NotificationHub at /notifications broadcasts ReceiveChangeNotification(projectId, projectName, count) to all clients.
Backup before sync: Use timestamped naming {filename}_{yyyyMMdd_HHmmss}.bak.
Service API Endpoints
GET/POST/PUT/DELETE /api/projects- Project CRUDGET /api/changes/{projectId}- Get pending changesGET /api/changes/{projectId}/history- Get synced/ignored changesPOST /api/sync- Sync selected changesPOST /api/ignore- Ignore selected changes
Database
- SQLite with WAL mode
- Entities:
ProjectConfig,FileRevision,PendingChange ProjectConfig.FileExtensionsis comma-separated (e.g., ".jt,.cojt,.xml")
Solution Structure
EngineeringSync.slnx
├── EngineeringSync.Domain/ (Class Library, net10.0) - Entities, Enums, Interfaces
├── EngineeringSync.Infrastructure/ (Class Library, net10.0) - EF Core, DbContext
├── EngineeringSync.Service/ (Worker Service, net10.0) - Kestrel on :5050, SignalR hub
├── EngineeringSync.TrayApp/ (WPF App, net10.0-windows) - System tray + UI windows
├── EngineeringSync.Setup/ (WPF App, net10.0-windows) - Setup wizard + Installer
└── installer/
├── setup.iss - Inno Setup Script
└── build-installer.ps1 - Build + Publish + ISCC Pipeline
Tech Stack
- Framework: .NET 10
- Service: Worker Service (Windows Service) + ASP.NET Core Minimal API + SignalR
- Client: WPF (.NET 10-Windows) with
H.NotifyIcon.Wpf+CommunityToolkit.Mvvm - Database: SQLite via EF Core 10 (WAL mode)
- File Watching:
System.IO.FileSystemWatcher+System.Threading.Channels(debouncing)
Key Architecture
- ProjectConfig in DB: Managed via TrayApp UI, stored in SQLite. User adds/edits/deletes projects with folder browser dialogs. CRUD operations go through Service API, which dynamically starts/stops watchers.
- FileSystemWatcher Debouncing: Events (Created, Changed, Renamed, Deleted) pushed into
Channel<FileEvent>. Consumer groups events by(ProjectId, RelativePath)within 2000ms sliding window. SHA-256 hashing againstFileRevisionconfirms actual changes before writing aPendingChange. - SignalR Hub:
NotificationHubat/notificationsbroadcastsReceiveChangeNotification(projectId, projectName, count)andProjectConfigChanged().
Notes
- Domain project has no dependencies (pure entities/enums)
- Infrastructure depends on Domain
- Service depends on Domain + Infrastructure
- TrayApp depends on Domain + Service (via HTTP client)