Initial Commit after switching from SVN to git
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
---
|
||||
applyTo: "Fuchs_DataService/**"
|
||||
---
|
||||
|
||||
# Fuchs_DataService Instructions
|
||||
|
||||
## Overview
|
||||
`Fuchs_DataService` is a .NET 10 Windows Service (via **Topshelf**) that synchronises MFR entity data into the Fuchs SQL database on a configurable schedule.
|
||||
|
||||
## Key Classes
|
||||
| Class | Role |
|
||||
|-------|------|
|
||||
| `FdsMainModule` | Entry point — builds `LoggerFactory`, creates `FdsService`, runs Topshelf |
|
||||
| `FdsService` | Topshelf `ServiceControl` — owns `PeriodicHostedService` lifecycle |
|
||||
| `PeriodicHostedService` | Runs one or more `PeriodicJobDefinition` jobs independently |
|
||||
| `FdsMfr : IFdsMfr` | Singleton business logic — registered via DI constructor injection |
|
||||
| `FdsMfrClient` | Wraps `MFRClient` + SQL write logic; instantiated per-use inside `FdsMfr` |
|
||||
| `Archive` (FdsZip) | SevenZip wrapper; created per-use, accepts optional `ILogger<Archive>` |
|
||||
| `FdsConfig` | Static config accessor — reads from `appsettings.json` under `Fds:` key |
|
||||
| `FdsShared` | Static SQL/stream helpers — use `FdsDebug.DebugLog` for errors in static methods |
|
||||
| `FdsDebug` | Legacy static logger — **only for use in static helper methods** |
|
||||
| `FdsLoggerProvider` | Custom `ILoggerProvider`: Debug output + file + prepared DB logging |
|
||||
|
||||
## Dependency Injection
|
||||
`FdsMfr` is the main business singleton. It receives:
|
||||
- `ILogger<FdsMfr>` — for its own logging
|
||||
- `ILoggerFactory` — to create loggers for `FdsMfrClient` and `Archive` it instantiates
|
||||
|
||||
```csharp
|
||||
// FdsService constructor
|
||||
var mfr = new FdsMfr(loggerFactory.CreateLogger<FdsMfr>(), loggerFactory);
|
||||
```
|
||||
|
||||
## Configuration (`appsettings.json`)
|
||||
```json
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"fuchs_fds_ConnectionString": "...",
|
||||
"fuchs_ConnectionString": "..."
|
||||
},
|
||||
"Fds": {
|
||||
"ExecutionFrequency_Minutes": 15,
|
||||
"DebugDetails": false,
|
||||
"MFR_UserName": "...",
|
||||
"MFR_Password": "...",
|
||||
"MFR_host": "https://..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Do **not** use `System.Configuration.ConfigurationManager` — always use `FdsConfig` / `IConfiguration`.
|
||||
- Do **not** use `OCMS` or `OCMS_sharp`.
|
||||
- Use `FdsSqlOptions` for all SQL calls (`getSQLDatatable_async`, `getSQLDataSet_async`, `setSQLValue_async`).
|
||||
- Classes that run async work must use `async/await` — never `.Wait()` or `.Result` in new code.
|
||||
- Add new periodic jobs as `PeriodicJobDefinition` entries in `FdsService` — see `periodic_service.instructions.md`.
|
||||
- Logging: see `logging.instructions.md`.
|
||||
@@ -0,0 +1,176 @@
|
||||
|
||||
# Frontend Instructions
|
||||
|
||||
## General Rules
|
||||
|
||||
- **Never edit files inside `wwwroot/` directly.** All output files are generated by the Gulp build pipeline.
|
||||
- All stylesheet sources must be written in **SCSS** (`.scss`), not plain CSS.
|
||||
- After adding or changing source files, ensure they are listed in `Fuchs/bdlconfig.json` so they are picked up by the build.
|
||||
|
||||
## Source File Locations
|
||||
|
||||
| Type | Source path |
|
||||
|------|-------------|
|
||||
| SCSS (shared/global) | `Fuchs/css/intranet/` |
|
||||
| SCSS (module-specific) | `Fuchs/js/intranet/modules/<module>/` (co-located with the module JS) |
|
||||
| JavaScript (core) | `Fuchs/js/intranet/` |
|
||||
| JavaScript (modules) | `Fuchs/js/intranet/modules/<module>/` |
|
||||
|
||||
## Output / Bundle Locations
|
||||
|
||||
All bundles are written to `Fuchs/web/` by the Gulp pipeline. Do not reference these paths as source.
|
||||
|
||||
| Bundle | Context | Description |
|
||||
|--------|---------|-------------|
|
||||
| `web/fisb.min.css` | `intranet` | Bootstrap/login CSS |
|
||||
| `web/fis.min.css` | `intranet` | Main intranet CSS |
|
||||
| `web/fis.inv.min.css` | `intranet:inv` | Invoices module CSS |
|
||||
| `web/fis.req.min.css` | `intranet:req` | Requests module CSS |
|
||||
| `web/fis.rep.min.css` | `intranet:rep` | Reports module CSS |
|
||||
| `web/fis.bam.min.css` | `intranet:bam` | BAM module CSS |
|
||||
| `web/fisb.min.js` | `intranet` | Bootstrap/basic JS |
|
||||
| `web/fis.min.js` | `intranet` | Main intranet JS |
|
||||
| `web/fis.inv.de.js` | `intranet:inv` | Invoices module JS |
|
||||
| `web/fis.req.de.js` | `intranet:req` | Requests module JS |
|
||||
| `web/fis.rep.de.js` | `intranet:rep` | Reports module JS |
|
||||
| `web/fis.bam.de.js` | `intranet:bam` | BAM module JS |
|
||||
|
||||
## bdlconfig.json Entry Format
|
||||
|
||||
`Fuchs/bdlconfig.json` drives all bundle definitions. Each entry has this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"context": "intranet",
|
||||
"outputFileName": "web/<bundle-name>.min.css",
|
||||
"inputFiles": [
|
||||
"css/intranet/oci_variables.scss",
|
||||
"css/intranet/fis_variables.scss",
|
||||
"css/intranet/<your-file>.scss"
|
||||
],
|
||||
"minify": { "enabled": true }
|
||||
}
|
||||
```
|
||||
|
||||
### SCSS Variable Prepend Rule
|
||||
|
||||
Every CSS bundle **must** include the two variable files as the first `inputFiles` entries, in this order:
|
||||
|
||||
```
|
||||
css/intranet/oci_variables.scss
|
||||
css/intranet/fis_variables.scss
|
||||
```
|
||||
|
||||
These define the shared design tokens used across all SCSS files. Omitting them will cause compilation errors.
|
||||
|
||||
### Module Co-location Pattern
|
||||
|
||||
Module-specific SCSS files live alongside their JavaScript counterparts under `js/intranet/modules/<module>/`. When adding a new module:
|
||||
|
||||
1. Create `js/intranet/modules/<module>/<module>.scss` for module styles.
|
||||
2. Add that path to the appropriate context bundle in `bdlconfig.json` (after the variable files).
|
||||
3. Create `js/intranet/modules/<module>/<module>.js` for module logic.
|
||||
4. Add that path to the corresponding JS bundle in `bdlconfig.json`.
|
||||
|
||||
## Build Pipeline
|
||||
|
||||
The Gulp 4 pipeline (`Fuchs/gulpfile.js`) reads `bdlconfig.json` and `copyconfig.json`.
|
||||
|
||||
### Available Tasks
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `gulp min:scss` | Compile and minify all SCSS bundles |
|
||||
| `gulp min:js` | Concatenate and minify all JS bundles |
|
||||
| `gulp min:html` | Minify HTML bundles |
|
||||
| `gulp min` | Run `min:js`, `min:scss`, and `min:html` in parallel |
|
||||
| `gulp copy` | Run file copy tasks from `copyconfig.json` |
|
||||
| `gulp all` | Run `min` and `copy` in parallel (full build) |
|
||||
| `gulp clean` | Delete all bundle output files |
|
||||
| `gulp watch` | Watch source files and rebuild on change |
|
||||
|
||||
Run from the `Fuchs/` directory:
|
||||
|
||||
```powershell
|
||||
cd Fuchs
|
||||
npx gulp all
|
||||
```
|
||||
|
||||
### How SCSS Compilation Works
|
||||
|
||||
- All `.scss` files listed in a bundle's `inputFiles` are concatenated into a single stream, then compiled with `node-sass` via `gulp-sass`.
|
||||
- The output is then minified with `gulp-cssmin` and written to `web/<bundle>.min.css`.
|
||||
- `.less` files in the same bundle are compiled separately and merged with the SCSS output before minification.
|
||||
|
||||
### How JS Bundling Works
|
||||
|
||||
- All JS files listed in `inputFiles` are concatenated with `gulp-concat`.
|
||||
- Files listed in `inputFiles_tominify` are individually minified with `gulp-terser` before concatenation.
|
||||
- When `minify.enabled` is `true` (default), the full bundle is also minified via `gulp-terser`.
|
||||
|
||||
## npm Dependencies
|
||||
|
||||
Runtime packages available via npm (do not copy these into source manually):
|
||||
|
||||
| Package | Usage |
|
||||
|---------|-------|
|
||||
| `jquery` | DOM/AJAX |
|
||||
| `js-cookie` | Cookie access |
|
||||
| `fg-loadcss` | Async CSS loading (`loadCSS.js`, `onloadCSS_array.js`) |
|
||||
| `tinymce` | Rich text editor |
|
||||
|
||||
These are copied to `web/` or `wwwroot/lib/` via `copyconfig.json` entries and the `gulp copy` task.
|
||||
|
||||
| Package | Destination (gulp copy) | Served at |
|
||||
|---------|------------------------|-----------|
|
||||
| `jquery` + `js-cookie` | concatenated into `Fuchs/web/tools.js` | `~/web/tools.js` |
|
||||
| `tinymce` | `wwwroot/lib/tinymce/` | `~/lib/tinymce/tinymce.min.js` |
|
||||
|
||||
## Layout Asset Wiring (`Views/Shared/_Layout.cshtml`)
|
||||
|
||||
Asset loading follows a strict order and is split by authentication state.
|
||||
|
||||
### Always loaded (before auth check)
|
||||
|
||||
```razor
|
||||
<script src="~/web/tools.js" asp-append-version="true"></script>
|
||||
```
|
||||
|
||||
`tools.js` contains jQuery and js-cookie. It must be first so all subsequent scripts can use `$` and `Cookies`.
|
||||
|
||||
### Auth-conditional bundles
|
||||
|
||||
```razor
|
||||
@if (isAuth)
|
||||
{
|
||||
<script src="~/lib/tinymce/tinymce.min.js"></script>
|
||||
<link rel="stylesheet" href="~/web/fis.min.css" asp-append-version="true" />
|
||||
<script src="~/web/fis.min.js" asp-append-version="true"></script>
|
||||
}
|
||||
else
|
||||
{
|
||||
<link rel="stylesheet" href="~/web/fisb.min.css" asp-append-version="true" />
|
||||
<script src="~/web/fisb.min.js" asp-append-version="true"></script>
|
||||
}
|
||||
```
|
||||
|
||||
- **Authenticated** (`fis.*`): full intranet styles + scripts + TinyMCE rich-text editor.
|
||||
- **Unauthenticated** (`fisb.*`): login-page-only styles + scripts. TinyMCE is **not** loaded.
|
||||
- TinyMCE is always loaded from `~/lib/tinymce/tinymce.min.js` (copied by `gulp copy` from `node_modules/tinymce`). Never use `~/Scripts/tinymce/` — that path does not exist.
|
||||
|
||||
### `$ocms.auth` injection (always)
|
||||
|
||||
A `<script>` block is always rendered (authenticated or not) that writes the `$ocms.auth` object with the current user's id, email, and authorization level. This lets client-side code read auth state without additional requests.
|
||||
|
||||
### Per-page module bundles (`@section CustomHeader`)
|
||||
|
||||
Module bundles (`fis.inv`, `fis.req`, `fis.rep`, `fis.bam`) are **not** loaded globally. Each view that needs them renders a `@section CustomHeader` block:
|
||||
|
||||
```razor
|
||||
@section CustomHeader {
|
||||
<link rel="stylesheet" href="~/web/fis.inv.min.css" asp-append-version="true" />
|
||||
<script src="~/web/fis.inv.de.js" asp-append-version="true"></script>
|
||||
}
|
||||
```
|
||||
|
||||
`_Layout.cshtml` renders `@RenderSection("CustomHeader", required: false)` inside `<head>`, so only the relevant module assets are fetched for each page.
|
||||
@@ -0,0 +1,75 @@
|
||||
|
||||
|
||||
# JavaScript Instructions
|
||||
|
||||
## General Rules
|
||||
|
||||
- **Never edit files inside `wwwroot/` directly.** All JS output files are generated by the Gulp build pipeline.
|
||||
- After adding or modifying JS source files, ensure they are listed in `Fuchs/bdlconfig.json` under the correct bundle and context.
|
||||
|
||||
## Source File Locations
|
||||
|
||||
| Type | Path |
|
||||
|------|------|
|
||||
| Core intranet JS | `Fuchs/js/intranet/` |
|
||||
| Feature module JS | `Fuchs/js/intranet/modules/<module>/` |
|
||||
|
||||
## Bundle Contexts
|
||||
|
||||
Each bundle in `bdlconfig.json` has a `context` field that determines which page(s) load it:
|
||||
|
||||
| Context | Loaded on |
|
||||
|---------|-----------|
|
||||
| `intranet` | All intranet pages |
|
||||
| `intranet:inv` | Invoices module only |
|
||||
| `intranet:req` | Requests module only |
|
||||
| `intranet:rep` | Reports module only |
|
||||
| `intranet:bam` | BAM module only |
|
||||
|
||||
## Adding a JS File to a Bundle
|
||||
|
||||
Locate the correct bundle entry in `Fuchs/bdlconfig.json` by matching the `context` and `outputFileName`, then add the source path to `inputFiles`:
|
||||
|
||||
```json
|
||||
{
|
||||
"context": "intranet:inv",
|
||||
"outputFileName": "web/fis.inv.de.js",
|
||||
"inputFiles": [
|
||||
"js/intranet/modules/invoices/invoices.js",
|
||||
"js/intranet/modules/invoices/your-new-file.js"
|
||||
],
|
||||
"minify": { "enabled": true }
|
||||
}
|
||||
```
|
||||
|
||||
Files listed in `inputFiles_tominify` are minified individually before concatenation (use for third-party scripts that should be minified separately).
|
||||
|
||||
## Adding a New Module
|
||||
|
||||
1. Create `Fuchs/js/intranet/modules/<module>/<module>.js`.
|
||||
2. If the module needs styles, create `Fuchs/js/intranet/modules/<module>/<module>.scss`.
|
||||
3. Add both files to the appropriate bundle entries in `bdlconfig.json`.
|
||||
4. Run `npx gulp all` from the `Fuchs/` directory to rebuild.
|
||||
|
||||
## npm Packages
|
||||
|
||||
Use packages already declared in `Fuchs/package.json` where possible. Available runtime packages:
|
||||
|
||||
| Package | Global / Usage |
|
||||
|---------|----------------|
|
||||
| `jquery` | DOM, AJAX |
|
||||
| `js-cookie` | Cookie read/write |
|
||||
| `fg-loadcss` | Async CSS loading |
|
||||
| `tinymce` | Rich text editor |
|
||||
|
||||
Do not import npm packages directly in source files — they are copied to `web/` by `gulp copy` and referenced via bundle entries in `bdlconfig.json`.
|
||||
|
||||
## Build Commands
|
||||
|
||||
Run from the `Fuchs/` directory:
|
||||
|
||||
```powershell
|
||||
cd Fuchs
|
||||
npx gulp min:js # rebuild JS bundles only
|
||||
npx gulp all # full rebuild (JS + CSS + copy)
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
applyTo: "Fuchs/**,Fuchs_DataService/**,MFR_RESTClient/**,MT940Parser/**"
|
||||
---
|
||||
|
||||
# Logging Instructions
|
||||
|
||||
## Overview
|
||||
All logging in this solution uses `Microsoft.Extensions.Logging.ILogger<T>`.
|
||||
**Never use** `System.Diagnostics.Debug.Print`, `Debug.WriteLine`, `Console.WriteLine`, or `OCMS.ocms_debug.debug_log` in business or library code.
|
||||
|
||||
## Providers
|
||||
|
||||
### Fuchs_DataService — `FdsLoggerProvider`
|
||||
- Location: `Fuchs_DataService/Logging/FdsLoggerProvider.cs`
|
||||
- Always active: **Debug output** (`System.Diagnostics.Debug.WriteLine`) + **file** (`tmp/DebugLog.txt`, `tmp/ErrorLog.txt`)
|
||||
- Database logging: prepared via `WriteToDatabase()` but **disabled by default**. Activate with `FdsLoggerProvider.DatabaseLoggingEnabled = true`.
|
||||
- Registered in `FdsMain.cs` via `LoggerFactory.Create(b => b.SetMinimumLevel(LogLevel.Debug).AddFdsLogging())`.
|
||||
|
||||
### Fuchs — `FuchsLoggerProvider`
|
||||
- Location: `Fuchs/Logging/FuchsLoggerProvider.cs`
|
||||
- Always active: **Debug output** + **file** (`logs/AppLog.txt`, `logs/ErrorLog.txt`)
|
||||
- Database logging: prepared but **disabled by default**. Activate with `FuchsLoggerProvider.DatabaseLoggingEnabled = true`.
|
||||
- Registered in `Program.cs` via `builder.Logging.SetMinimumLevel(LogLevel.Debug).AddFuchsLogging()`.
|
||||
|
||||
### MFR_RESTClient — Library (no provider)
|
||||
- No logging provider — purely a library.
|
||||
- `MFRClient` accepts `ILogger<MFRClient>?` as an optional constructor parameter.
|
||||
- Defaults to `NullLogger<MFRClient>.Instance` when no logger is provided.
|
||||
- Callers supply a logger from the host's `ILoggerFactory`.
|
||||
|
||||
### MT940Parser — Library (no provider)
|
||||
- Location: `../../WebProjectComponents/MT940Parser/` (shared external component)
|
||||
- No logging provider — purely a library.
|
||||
- **Public entry point only**: `Parser` accepts `ILogger<Parser>?` as an optional constructor parameter and defaults to `NullLogger<Parser>.Instance`.
|
||||
- **Internal sub-parsers** (`StatementParser`, `BalanceParser`, `StatementLineParser`, `AdditionalInfoParser`) are pure algorithmic classes. They **do not hold logger fields**. Parse errors are thrown as exceptions (`InvalidDataException`, `FormatException`) and caught by `Parser.Parse()`, which logs them at `LogWarning`.
|
||||
- Callers supply a logger when constructing `Parser`; the sub-parsers never need one directly.
|
||||
|
||||
## Rules
|
||||
- **DI classes** (singletons, scoped): receive `ILogger<T>` via constructor injection.
|
||||
- **Library classes** (`MFRClient`, `Parser`): accept `ILogger<T>?` as an optional constructor parameter, default to `NullLogger<T>.Instance`.
|
||||
- **Internal library helpers** (`StatementParser`, `BalanceParser`, `StatementLineParser`, `AdditionalInfoParser`): pure algorithmic, no logger field. Throw exceptions; the public entry-point catches and logs.
|
||||
- **Static helpers** (`FdsShared`, `Banking`): accept `ILogger?` as an optional method parameter, or delegate to `FdsDebug.DebugLog` for infrastructure-level errors.
|
||||
- `FdsDebug` remains the **fallback for static infrastructure code only** (stream helpers, etc.). Do not use it in new class-level code.
|
||||
- Use structured logging parameters: `_logger.LogError(ex, "Message {Param}", value)` — never string interpolation in the message template.
|
||||
|
||||
## Log Levels
|
||||
| Level | Use for |
|
||||
|-------|---------|
|
||||
| `LogDebug` | Timing, row counts, progress traces |
|
||||
| `LogInformation` | Service start/stop, significant state changes |
|
||||
| `LogWarning` | Recoverable issues, skipped items |
|
||||
| `LogError` | Caught exceptions, data sync failures |
|
||||
| `LogCritical` | Unrecoverable failures that stop a job |
|
||||
|
||||
## Enabling Database Logging
|
||||
```csharp
|
||||
// Fuchs_DataService
|
||||
FdsLoggerProvider.DatabaseLoggingEnabled = true;
|
||||
|
||||
// Fuchs web
|
||||
FuchsLoggerProvider.DatabaseLoggingEnabled = true;
|
||||
```
|
||||
Uncomment the body of `WriteToDatabase()` in the respective provider and adjust the stored procedure name.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
applyTo: "Fuchs_DataService/**"
|
||||
---
|
||||
|
||||
# PeriodicHostedService Instructions
|
||||
|
||||
## Overview
|
||||
`PeriodicHostedService` (`Fuchs_DataService/PeriodicHostedService.cs`) is a `BackgroundService` that runs multiple independent jobs, each on its own `PeriodicTimer`. It is started and stopped by the Topshelf `FdsService` (see `topshelf.instructions.md`).
|
||||
|
||||
## Job Definition
|
||||
Each job is a `PeriodicJobDefinition` record:
|
||||
```csharp
|
||||
public sealed record PeriodicJobDefinition(
|
||||
string Name,
|
||||
TimeSpan Interval,
|
||||
Func<CancellationToken, Task> Execute);
|
||||
```
|
||||
|
||||
## Registering Jobs
|
||||
Jobs are created in `FdsService` constructor and passed to `PeriodicHostedService`:
|
||||
```csharp
|
||||
var interval = TimeSpan.FromMinutes(FdsConfig.ExecutionFrequency_Minutes);
|
||||
var jobs = new[]
|
||||
{
|
||||
new PeriodicJobDefinition("MfrSync", interval, async ct =>
|
||||
{
|
||||
await mfr.UpdateIfNecessary_async(debug);
|
||||
await mfr.UpdateRequested_async(debug);
|
||||
await mfr.GetInvoiceFiles_async(debug);
|
||||
}),
|
||||
// Add more jobs with different schedules here:
|
||||
// new PeriodicJobDefinition("HourlyJob", TimeSpan.FromHours(1), async ct => { ... })
|
||||
};
|
||||
```
|
||||
|
||||
## Schedules
|
||||
- Each job runs on its **own independent `PeriodicTimer`** — schedules do not interfere.
|
||||
- Frequencies are configured in `appsettings.json` under the `Fds` section (e.g. `Fds:ExecutionFrequency_Minutes`).
|
||||
- Add new config keys for additional job intervals as needed.
|
||||
|
||||
## Error Handling
|
||||
- Each job's `Execute` delegate is wrapped in a `try/catch` inside `RunJobAsync`.
|
||||
- `OperationCanceledException` propagates normally (signals shutdown).
|
||||
- All other exceptions are caught, logged via `ILogger`, and the job continues on its next tick.
|
||||
|
||||
## Cancellation
|
||||
- `PeriodicHostedService` respects the `CancellationToken` passed to `StartAsync`.
|
||||
- `FdsService.Stop()` cancels the token, then awaits `StopAsync(CancellationToken.None)`.
|
||||
|
||||
## Adding a New Job
|
||||
1. Optionally add a new frequency key to `appsettings.json` and read it via `FdsConfig`.
|
||||
2. Add a new `PeriodicJobDefinition` to the `jobs` array in `FdsService`.
|
||||
3. No changes to `PeriodicHostedService` itself are needed.
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
applyTo: "Fuchs_DataService/**"
|
||||
---
|
||||
|
||||
# Topshelf Instructions
|
||||
|
||||
## Overview
|
||||
`Fuchs_DataService` uses **Topshelf 4.x** as the Windows Service host. The hosted jobs run inside a `PeriodicHostedService` (see `periodic_service.instructions.md`), which is managed by the Topshelf `FdsService` class.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
FdsMainModule.Main()
|
||||
└── HostFactory.Run()
|
||||
└── FdsService : ServiceControl
|
||||
├── Start() → _hostedService.StartAsync(_cts.Token)
|
||||
└── Stop() → _cts.Cancel() + _hostedService.StopAsync()
|
||||
```
|
||||
|
||||
## FdsService Rules
|
||||
- `FdsService` owns a `CancellationTokenSource _cts` for cooperative cancellation.
|
||||
- `FdsService` builds the `ILoggerFactory` (via `LoggerFactory.Create(b => b.AddFdsLogging())`) and passes it to all job objects.
|
||||
- `FdsService` constructs `FdsMfr` directly: `new FdsMfr(loggerFactory.CreateLogger<FdsMfr>(), loggerFactory)`.
|
||||
- `FdsService` constructs `PeriodicHostedService` with jobs and passes `loggerFactory.CreateLogger<PeriodicHostedService>()`.
|
||||
- Never use the generic `Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder` pattern here — Topshelf manages the host lifetime.
|
||||
|
||||
## Topshelf Configuration
|
||||
```csharp
|
||||
HostFactory.Run(x =>
|
||||
{
|
||||
x.Service<FdsService>(s =>
|
||||
{
|
||||
s.ConstructUsing(name => new FdsService());
|
||||
s.WhenStarted((tc, host) => tc.Start(host));
|
||||
s.WhenStopped((tc, host) => tc.Stop(host));
|
||||
s.WhenPaused((tc, host) => tc.Stop(host));
|
||||
s.WhenContinued((tc, host) => tc.Start(host));
|
||||
});
|
||||
x.EnablePauseAndContinue();
|
||||
x.StartAutomatically();
|
||||
x.RunAsLocalSystem();
|
||||
x.SetDescription("MFR Data Sync");
|
||||
x.SetDisplayName("MFR Data Sync");
|
||||
x.SetServiceName("MFR Data Sync");
|
||||
});
|
||||
```
|
||||
|
||||
## Dev-Machine Shortcut
|
||||
On machines named `digital-pc` or `digital-dpc` the service runs in-process (console) instead of installing as a Windows Service.
|
||||
|
||||
## Adding a New Job
|
||||
Add a new `PeriodicJobDefinition` in `FdsService` constructor — see `periodic_service.instructions.md`.
|
||||
Reference in New Issue
Block a user