FubarDev.Lexware.Configuration.Json
0.7.0
Prefix Reserved
dotnet add package FubarDev.Lexware.Configuration.Json --version 0.7.0
NuGet\Install-Package FubarDev.Lexware.Configuration.Json -Version 0.7.0
<PackageReference Include="FubarDev.Lexware.Configuration.Json" Version="0.7.0" />
<PackageVersion Include="FubarDev.Lexware.Configuration.Json" Version="0.7.0" />
<PackageReference Include="FubarDev.Lexware.Configuration.Json" />
paket add FubarDev.Lexware.Configuration.Json --version 0.7.0
#r "nuget: FubarDev.Lexware.Configuration.Json, 0.7.0"
#:package FubarDev.Lexware.Configuration.Json@0.7.0
#addin nuget:?package=FubarDev.Lexware.Configuration.Json&version=0.7.0
#tool nuget:?package=FubarDev.Lexware.Configuration.Json&version=0.7.0
FubarDev.Lexware
.NET-Bibliotheken für den Zugriff auf Daten der Lexware-Desktopanwendung.
Übersicht
Lexware Financial Office speichert seine Daten je nach Version in einer SAP SQL Anywhere- (bis Lexware 2025) oder PostgreSQL-Datenbank (ab Lexware 2026). Eine direkte Schnittstelle für maschinell lesbare Datenexporte existiert nicht. Dieses Projekt stellt ein .NET-10-Bibliothekspaket bereit, das den vollständigen Zugriff auf diese Daten ermöglicht — von der Verbindungsaufbau-Initialisierung bis hin zu NHibernate-Sessions auf die Entitymodelle.
Bibliotheken
| Paket | Inhalt |
|---|---|
FubarDev.Lexware.Database.Abstractions |
Schnittstellen für Connection- und Session-Factory (ILexwareConnectionFactory, ILexwareSessionFactoryFactory), Credential-Generator, Deskriptoren für globale Lexware-Datenbanken (GlobalDatabase, GlobalDatabaseKind); NHibernate-Hilfsmittel (LxDateOnlyType / LxDateOnlyCompatType mit Alias LxDateOnly, ILexwareSessionFactoryConfigurator, LexwareConfiguratorOrder) |
FubarDev.Lexware.Database.SqlAnywhere |
NHibernate-Session-Factory für SAP SQL Anywhere 17 |
FubarDev.Lexware.Database.Postgres |
NHibernate-Session-Factory für PostgreSQL |
FubarDev.Lexware.Entities.LxOffice |
NHibernate-Entities für die lxoffice-Datenbank (Benutzer, Firmen) |
FubarDev.Lexware.Entities.LxCompany |
NHibernate-Entities für die Firmendatenbanken (f1, f2, …) |
FubarDev.Lexware.Entities.LxGlobal |
NHibernate-Entities für die globalen Lexware-Datenbanken (PoC: aktuell Feiertage aus der UK-DB) |
FubarDev.Lexware.Configuration.Abstractions |
Schnittstellen für Konfigurationspersistierung (ILexwareConfigurationStore, ILexwareConfigurationProvider, ILexwareConfigurationSource) |
FubarDev.Lexware.Configuration.Json |
JSON-Datei-basierte Konfiguration mit transparenter Verschlüsselung sensitiver Werte |
FubarDev.Lexware.InstallationConfiguration |
Refreshbarer Konfigurations-Cache für vom Lexware-Network-Share abgeleitete Werte (XML-Setup, PropertyStore, pin2.bin, postgresql.dd) mit transparenter Verschlüsselung sensitiver Schlüssel |
FubarDev.Lexware.Service |
Refit-HTTP-Client für den lokalen LexwareService (PostgreSQL) |
FubarDev.Lexware.Client |
High-Level-Client: Initialisierung, Login, Session-Management |
FubarDev.Lexware.Client zieht die meisten Pakete als transitive Abhängigkeiten mit.
FubarDev.Lexware.Configuration.Json muss separat referenziert werden.
Beispielanwendung
Das Repository enthält samples/lx-access — ein CLI-Tool das zeigt, wie die
Bibliotheken zusammenspielen.
Das Tool ist aufgrund seiner Größe nicht im NuGet-Registry verfügbar. Es kann direkt aus den Release-Assets installiert werden:
dotnet tool install --add-source <pfad-zum-release-verzeichnis> lx-access
Voraussetzungen
Lexware Financial Office lokal oder über Netzwerkfreigabe erreichbar
.NET 10 SDK
CP1252-Encoding registrieren — Lexware-Datenbanken verwenden die Codepage Windows-1252. Da .NET Core/5+ diese nicht standardmäßig enthält, muss das NuGet-Paket
System.Text.Encoding.CodePagesreferenziert und der Provider vor dem ersten Datenbankzugriff registriert werden:using System.Text; Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);Für SQL Anywhere:
Sap.Data.SQLAnywhere.Core.v2.1.dllaus der lokalen SA17-Installation (SAP-Lizenz, nicht im Repository enthalten — siehe CONTRIBUTING.md). Der Pfad zum Verzeichnis, das die DLL enthält, wird über den KonfigurationsschlüsselLexware:Database:SqlAnywhere:AdoNetAssemblyDirectorygesetzt — z.B. perappsettings.json:{ "Lexware": { "Database": { "SqlAnywhere": { "AdoNetAssemblyDirectory": "C:\\Program Files\\SQL Anywhere 17\\Assembly\\Core2.1" } } } }Die DLL befindet sich typischerweise im Unterverzeichnis
Assembly\Core2.1der SQL-Anywhere-17-Installation. Alternativ kann der Pfad per Umgebungsvariable gesetzt werden:DOTNET_Lexware__Database__SqlAnywhere__AdoNetAssemblyDirectory="C:\Program Files\SQL Anywhere 17\Assembly\Core2.1"Wird kein Pfad angegeben, greift .NET auf die Standard-Suchpfade zurück.
Installation
dotnet add package FubarDev.Lexware.Client
dotnet add package FubarDev.Lexware.Configuration.Json
FubarDev.Lexware.Client zieht FubarDev.Lexware.Configuration.Abstractions als
transitive Abhängigkeit mit. Das JSON-Konfigurationspaket wird separat referenziert
und stellt den JsonFileLexwareConfigurationStoreProvider bereit.
Schnellstart
Die folgenden Beispiele sind als eigenständige C#-Skripte konzipiert und können
direkt mit dotnet run <datei>.cs ausgeführt werden (.NET 10 SDK).
Lifecycle beim App-Start
ILexwareInstallationRefresher.RefreshAsync ist der empfohlene Lifecycle-Einstiegspunkt
für jeden App-Start. Der Aufruf bündelt drei Schritte: er liest den PropertyStore und die
abgeleiteten Werte (XML-Setup, *.ps, pin2.bin, postgresql.dd) erneut von der
Lexware-Freigabe in den Installation-Cache, holt bei PostgreSQL-Installationen ein
Client-Zertifikat (falls keines vorliegt), und erneuert Client- bzw. Root-Zertifikat,
wenn NotAfter höchstens einen Tag entfernt liegt. Sensitive Werte werden im
Installation-Cache und im User-Konfigurationsstore transparent verschlüsselt;
IConfiguration liefert immer Klartext.
Voraussetzung ist, dass Lexware:Client:SharePath schon in der Konfiguration steht.
Der allererste Lauf einer App benötigt deshalb einmal den Bootstrap-Pfad (siehe
Erstkonfiguration unten).
#:package FubarDev.Lexware.Client
#:package FubarDev.Lexware.Configuration.Json
using FubarDev.Lexware.Client;
using FubarDev.Lexware.Configuration.Json;
using FubarDev.Lexware.InstallationConfiguration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var cacheFile = Path.Combine(Path.GetTempPath(), "lexware-installation.json");
var configFile = Path.Combine(Path.GetTempPath(), "lexware-configuration.json");
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddJsonLexwareConfiguration(configFile)
.AddJsonLexwareInstallationConfiguration(cacheFile);
builder.Services.AddLexwareClient();
var host = builder.Build();
await host.StartAsync();
var refresher = host.Services.GetRequiredService<ILexwareInstallationRefresher>();
if (refresher.GetState() != LexwareClientState.Uninitialized)
{
await refresher.RefreshAsync(
progress: new Progress<string>(Console.WriteLine),
cancellationToken: CancellationToken.None);
}
await host.StopAsync();
Das State-Gate hält den Refresh-Pfad sauber: solange noch kein
Lexware:Client:SharePath persistiert ist, gehört der Bootstrap-Pfad zum Zug
(siehe Erstkonfiguration unten). RefreshAsync selbst
prüft den Zustand nicht — der Konsument entscheidet, welcher Lifecycle-Pfad
greift.
Wer den frischen Konfigurations-Snapshot ohne Reload-Roundtrip braucht, kann
IInstallationConfigurationManager.Update() direkt aufrufen — die Methode liefert
einen ConfigurationUpdate (Discriminated Union Updated/Stale), dessen
Configuration-Payload den aktuellen Snapshot enthält. Für den Standard-App-Start ist
das aber nicht nötig; RefreshAsync orchestriert den Manager bereits intern und kümmert
sich zusätzlich um Cert-Provisioning und Renewal.
Erstkonfiguration
Beim allerersten Lauf einer App gibt es noch keinen Lexware:Client:SharePath im
Konfigurationsstore. ILexwareClient.InitializeAsync schreibt ihn dort ein, führt die
volle Initialisierung durch (Share-Read, Zertifikat-Provisioning für PostgreSQL,
Konnektivitätstest) und persistiert das Ergebnis. Anschließend reicht für jeden
weiteren App-Start der RefreshAsync-Pfad.
var client = host.Services.GetRequiredService<ILexwareClient>();
await client.InitializeAsync(
sharePath: @"\\server\lexware",
progress: new Progress<string>(Console.WriteLine),
cancellationToken: CancellationToken.None);
IsInitialized prüft, ob der SharePath bereits persistiert ist — geeignet für eine
einmalige Setup-Routine, die bei false einen interaktiven Dialog öffnet.
Login und Datenbankzugriff
#:package FubarDev.Lexware.Client
#:package FubarDev.Lexware.Configuration.Json
using FubarDev.Lexware.Client;
using FubarDev.Lexware.Client.Session;
using FubarDev.Lexware.Configuration.Json;
using FubarDev.Lexware.Entities.LxOffice;
using FubarDev.Lexware.Entities.LxCompany;
using FubarDev.Lexware.InstallationConfiguration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NHibernate.Linq;
var cacheFile = Path.Combine(Path.GetTempPath(), "lexware-installation.json");
var configFile = Path.Combine(Path.GetTempPath(), "lexware-configuration.json");
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddJsonLexwareConfiguration(configFile)
.AddJsonLexwareInstallationConfiguration(cacheFile);
builder.Services.AddLexwareClient();
var host = builder.Build();
await host.StartAsync();
// Installation auffrischen: Share-Read, Cert-Provisioning, Renewal-Check.
// State-Gate: Bei Uninitialized gehört der Bootstrap-Pfad zum Zug.
var refresher = host.Services.GetRequiredService<ILexwareInstallationRefresher>();
if (refresher.GetState() != LexwareClientState.Uninitialized)
await refresher.RefreshAsync(cancellationToken: CancellationToken.None);
var client = host.Services.GetRequiredService<ILexwareClient>();
var result = await client.LoginAsync("Supervisor", ".", CancellationToken.None);
if (result is SessionResult.Success { Session: ILexwareUserSession session })
{
using (session)
{
// Zugriff auf lxoffice (Benutzer, Firmen)
using var officeFactory = session.CreateOfficeSessionFactory();
using var officeSession = officeFactory.OpenSession();
var users = await officeSession.Query<LxGlobalUser>().ToListAsync();
Console.WriteLine($"{users.Count} Benutzer gefunden.");
// Zugriff auf eine Firmendatenbank
using var companyFactory = await session.CreateCompanySessionFactoryAsync(
companyId: 1,
CancellationToken.None);
using var companySession = companyFactory.OpenSession();
var orders = await companySession.Query<FkAuftrag>().ToListAsync();
Console.WriteLine($"{orders.Count} Aufträge in Firma 1.");
}
}
await host.StopAsync();
Alternativ — wenn der Caller nicht variant-spezifisch verzweigen will, sondern nur die
Session braucht oder bei jedem Auth-Fehler abbrechen möchte — kann
SessionResult.GetUserSessionOrThrow() verwendet werden. Die Methode liefert die
ILexwareUserSession bei Erfolg und wirft sonst LexwareAuthenticationException mit
einer aussagekräftigen Default-Message; das SessionResult bleibt über die Property
ex.SessionResult für Diagnose-Zwecke erhalten.
try
{
using var session = (await client.LoginAsync("Supervisor", ".", CancellationToken.None))
.GetUserSessionOrThrow();
using var officeFactory = session.CreateOfficeSessionFactory();
using var officeSession = officeFactory.OpenSession();
// …
}
catch (LexwareAuthenticationException ex)
{
Console.Error.WriteLine($"Login fehlgeschlagen: {ex.Message}");
}
Hinweis:
ILexwareUserSessionistIDisposable. Die Session hält intern einelxoffice-Session-Factory mit Login-Credential und wiederverwendet sie für Access-Checks und (bei SQL Anywhere) den Lookup der User-Metadaten beim Provisioning.Dispose()entsorgt die gehaltene Factory.
Mappings erweitern oder ersetzen
Eigene NHibernate-Entities können per DI-Registrierung eines
ILexwareSessionFactoryConfigurator eingebunden werden. Das Interface bietet drei
Hooks — für die lxoffice-Session-Factory, die Mandanten-Session-Factory und die
Session-Factory einer globalen Lexware-Datenbank. Jeder Configurator hat zusätzlich
eine Order-Property; Configurators laufen in aufsteigender Order. Default ist
LexwareConfiguratorOrder.Default (0); Late-Binding-Configurators (z.B. die
mitgelieferten Schema-Rewriter) laufen auf LexwareConfiguratorOrder.PostMapping
(1_000_000).
#:package FubarDev.Lexware.Client
#:package FubarDev.Lexware.Configuration.Json
using FubarDev.Lexware.Client;
using FubarDev.Lexware.Configuration.Json;
using FubarDev.Lexware.Database;
using FubarDev.Lexware.InstallationConfiguration;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NHibernate.Cfg;
var cacheFile = Path.Combine(Path.GetTempPath(), "lexware-installation.json");
var configFile = Path.Combine(Path.GetTempPath(), "lexware-configuration.json");
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddJsonLexwareConfiguration(configFile)
.AddJsonLexwareInstallationConfiguration(cacheFile);
builder.Services.AddLexwareClient();
// Eigene Entities zusätzlich laden:
builder.Services.AddSingleton<ILexwareSessionFactoryConfigurator, MyConfigurator>();
var host = builder.Build();
class MyConfigurator : ILexwareSessionFactoryConfigurator
{
// Optional: Reihenfolge übersteuern (Default: LexwareConfiguratorOrder.Default = 0).
// public int Order => LexwareConfiguratorOrder.Default;
public void ConfigureCompanySessionFactory(Configuration configuration, int companyId)
=> configuration.AddAssembly(typeof(MyCompanyEntity).Assembly);
// Weitere Hooks aus dem Interface (Default-Implementierungen verfügbar):
// public void ConfigureOfficeSessionFactory(Configuration configuration) { … }
// public void ConfigureGlobalDatabaseSessionFactory(Configuration configuration, GlobalDatabase database) { … }
}
Wird kein Konfigurator registriert oder werden keine Mappings gesetzt, greift der eingebaute Fallback auf die Standard-Entity-Assemblies.
Eigene Session-Factory-Factory
Wer eine eigene ILexwareSessionFactoryFactory mitbringt (z. B. für einen dritten
Datenbank-Treiber), muss sie als Snapshot über ein eigenes
ILexwareSessionFactoryFactoryFactory materialisieren und muss den NHibernate-Typedef-Alias
LxDateOnly selbst registrieren, bevor Standard-Entity-Mappings geladen werden. Für das
Snapshotting-Muster siehe
Session-Factory-Snapshotting.
Die mitgelieferten Standard-Implementierungen für den Alias sind:
LxDateOnlyType— bindetDateOnlynativ (für Treiber mit nativer Unterstützung, z. B. Npgsql)LxDateOnlyCompatType— bindetDateOnlyalsDateTime(für Treiber ohne native Unterstützung, z. B. SAP SQL Anywhere)
Registrierung in der eigenen Factory:
using FubarDev.Lexware.Database.NhSupport;
using NHibernate.Cfg;
using NHibernate.Cfg.Loquacious;
var cfg = new Configuration();
// ... weitere Konfiguration ...
// Eine der beiden Standard-Varianten — oder eine eigene IUserType-Implementierung:
cfg.TypeDefinition<LxDateOnlyType>(td => td.Alias = LexwareTypeAliases.DateOnly);
Der Alias wird in den Standard-HBM-Mappings als type="LxDateOnly" referenziert.
Ohne Registrierung schlägt BuildSessionFactory mit einem Mapping-Fehler fehl.
Konfiguration
Die Bibliotheken lesen ihre Einstellungen aus IConfiguration. Die meisten
Werte werden automatisch aus dem PropertyStore befüllt.
| Schlüssel | Beschreibung |
|---|---|
Lexware:Client:SharePath |
Pfad zur Lexware-Freigabe (z.B. \\server\lexware) |
Lexware:Server:Name |
Server-Name; falls nicht explizit gesetzt, leitet AddLexwareClient daraus die Hosts für LexwareService und SQL-Anywhere ab |
Lexware:Database:DatabaseType |
SqlAnywhere oder Postgres (aus PropertyStore) |
Lexware:Database:SqlAnywhere:DbPath |
Verzeichnis der SA-Datenbankdateien |
Lexware:Database:SqlAnywhere:AdoNetAssemblyDirectory |
Verzeichnis der Sap.Data.SQLAnywhere.Core.v2.1.dll |
Lexware:Database:Postgres:Addresses:0 |
PostgreSQL-Hostadresse (aus PropertyStore) |
Lexware:Database:Postgres:Port |
PostgreSQL-Port (aus PropertyStore) |
Lexware:LexwareService:Port |
Port des lokalen LexwareService (PostgreSQL, aus PropertyStore) |
Lexware:Product:Version |
Lexware-Versionsnummer (aus PropertyStore) |
Client-Zertifikat konfigurieren
Beim PostgreSQL-Backend fordert die Bibliothek ein Client-Zertifikat vom
LexwareService an. LexwareClientCertificateOptions macht die
Identitätsfelder konfigurierbar — AddLexwareClient bindet sie absichtlich
nicht automatisch, weil die Werte zum Konsumenten gehören, nicht zum
Lexware-Setup. Der Konsument bindet sie an einen App-spezifischen
Section-Namen:
services.AddOptions<LexwareClientCertificateOptions>()
.BindConfiguration("MyApp:Certificate");
Per-Property-Fallback auf interne Defaults: FriendlyName = "LexwareAccess",
CommonName = "LexwareAccess", OrganizationalUnit = "Fubar Development",
ValidDays = 365.
Zweck und beabsichtigte Nutzung
Diese Bibliothek wurde ausschließlich entwickelt, um Drittanwendungen die Interoperabilität mit lokal installierten, lizenzierten Lexware Financial Office-Installationen zu ermöglichen.
Lexware Financial Office stellt keinen vollständig maschinell lesbaren Datenexport für alle Datenkategorien bereit. Wo nur menschenlesbare Berichte verfügbar sind, ist der programmatische Zugriff auf die zugrundeliegende Datenbank der einzig gangbare Weg. Diese Bibliothek erlaubt es Drittanwendungen, sich mit denselben Zugangsdaten zu authentifizieren, die bereits in Lexware hinterlegt sind.
Die Bibliothek ist ausschließlich für lizenzierte Lexware-Nutzer gedacht, die auf ihre eigenen Daten aus ihrer eigenen lizenzierten Installation zugreifen möchten.
Rechtlicher Hinweis
Interoperabilität
Die in dieser Bibliothek implementierten Verfahren wurden durch Reverse Engineering zum alleinigen Zweck der Herstellung von Interoperabilität mit Lexware Financial Office gewonnen, wie es ausdrücklich durch § 69e UrhG (Urheberrechtsgesetz) und § 3 GeschGehG (Geschäftsgeheimnisgesetz) erlaubt ist.
Vertragliche Klauseln, die Reverse Engineering zu Interoperabilitätszwecken untersagen, sind nach § 69g Abs. 2 UrhG kraft Gesetzes unwirksam.
Nutzungsumfang
Diese Bibliothek wird ausschließlich zu Interoperabilitätszwecken bereitgestellt. Sie ist ausschließlich zur Nutzung mit lizenzierten Lexware-Installationen durch den jeweiligen Lizenznehmer zum Zugriff auf eigene Daten bestimmt. Jede Nutzung, die diesen Rahmen überschreitet — insbesondere der unberechtigte Zugriff auf fremde Systeme — ist kein unterstützter oder beabsichtigter Anwendungsfall und liegt in der alleinigen Verantwortung des Nutzers.
Keine Weitergabe von Lexware-Komponenten
Diese Bibliothek enthält, bündelt oder verteilt keine Komponente, Binary oder sonstige Asset von Lexware oder der Haufe Group.
Haftungsausschluss
DIE SOFTWARE WIRD OHNE MÄNGELGEWÄHR UND OHNE JEGLICHE AUSDRÜCKLICHE ODER STILLSCHWEIGENDE GEWÄHRLEISTUNG BEREITGESTELLT. DIE AUTOREN HAFTEN IN KEINEM FALL FÜR ANSPRÜCHE, SCHÄDEN ODER SONSTIGE VERBINDLICHKEITEN, DIE AUS DER NUTZUNG DER SOFTWARE ENTSTEHEN.
Lizenz
Copyright (c) 2026 Fubar Development Junker
Dieses Projekt steht unter der MIT-Lizenz.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. 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. |
-
net10.0
- FubarDev.Lexware.Configuration.Abstractions (>= 0.7.0)
- FubarDev.Lexware.Passwords (>= 0.6.0)
- Microsoft.Extensions.Configuration.Json (>= 10.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.