OpsFusion.Data
10.0.2
dotnet add package OpsFusion.Data --version 10.0.2
NuGet\Install-Package OpsFusion.Data -Version 10.0.2
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpsFusion.Data" Version="10.0.2" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpsFusion.Data" Version="10.0.2" />
<PackageReference Include="OpsFusion.Data" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpsFusion.Data --version 10.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: OpsFusion.Data, 10.0.2"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpsFusion.Data@10.0.2
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpsFusion.Data&version=10.0.2
#tool nuget:?package=OpsFusion.Data&version=10.0.2
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
OpsFusion.Data
OpsFusion.Data is the shared data-access foundation for OpsFusion services.
Use it to keep every project consistent in:
- Connection management
- Read vs write behavior
- Transaction boundaries
- Repository rules
- Main DB and log DB separation
Quick Start (Recommended)
- Install package:
dotnet add package OpsFusion.Data
- Add configuration keys shown in this README.
- Register in DI:
services.AddOpsFusionData(configuration). - Use
IUnitOfWorkRunnerin CQRS handlers. - Save via EF Core
SaveChangesAsyncorRunReadWriteAndSaveAsync(...).
What You Get
IUnitOfWorkFactory(main DB)IUnitOfWorkLogFactory(log DB)IUnitOfWorkRunner(scope runner)IUnitOfWorkSessionAccessor(current mode/session)BaseRepositoryWithKey<TEntity, TKey, TDbContext>BaseRepository<TEntity, TDbContext>AddOpsFusionData(...)DI extension
Core Concepts
1) Unit of Work Modes
ReadOnly: for query handlers.ReadWrite: for simple writes.ReadWriteWithTransaction: for multi-step atomic writes.
2) Session-bound Safety
Inside runner scope, repositories can detect mode. Writes in ReadOnly mode throw by design.
3) Main and Log Database Split
- Main domain data →
IUnitOfWorkFactory - Audit/log/telemetry data →
IUnitOfWorkLogFactory
Required Configuration
AddOpsFusionData resolves:
Database:ProviderLogStorage:Provider(optional, fallback =Database:Provider)
and provider-specific connection strings.
SQL Server
{
"Database": { "Provider": "SqlServer" },
"LogStorage": { "Provider": "SqlServer" },
"ConnectionStrings": {
"SqlServerReadOnly": "Server=...;Database=MainDb;User Id=...;Password=...;TrustServerCertificate=True",
"SqlServerWrite": "Server=...;Database=MainDb;User Id=...;Password=...;TrustServerCertificate=True",
"LogSqlServerReadOnly": "Server=...;Database=LogDb;User Id=...;Password=...;TrustServerCertificate=True",
"LogSqlServerWrite": "Server=...;Database=LogDb;User Id=...;Password=...;TrustServerCertificate=True"
}
}
PostgreSQL
{
"Database": { "Provider": "Postgres" },
"LogStorage": { "Provider": "Postgres" },
"ConnectionStrings": {
"PostgresReadOnly": "Host=...;Database=MainDb;Username=...;Password=...",
"PostgresWrite": "Host=...;Database=MainDb;Username=...;Password=...",
"LogPostgresReadOnly": "Host=...;Database=LogDb;Username=...;Password=...",
"LogPostgresWrite": "Host=...;Database=LogDb;Username=...;Password=..."
}
}
CQRS-First Usage (Recommended)
Query Handler (ReadOnly)
public sealed class GetCustomersQueryHandler(
IUnitOfWorkRunner unitOfWorkRunner,
ICustomerRepository customerRepository)
{
public Task<IReadOnlyList<CustomerEntity>> Handle(CancellationToken ct)
=> unitOfWorkRunner.RunReadOnlyAsync(
async token =>
{
IList<CustomerEntity> rows = await customerRepository.GetByFilterAsync(_ => true, track: false, cancellationToken: token);
return (IReadOnlyList<CustomerEntity>)rows.ToList();
},
ct);
}
Command Handler (ReadWrite + Save)
public sealed class CreateCustomerCommandHandler(
IUnitOfWorkRunner unitOfWorkRunner,
ICustomerRepository customerRepository,
PosInventoryDbContext dbContext)
{
public Task<Guid> Handle(CreateCustomerCommand command, CancellationToken ct)
=> unitOfWorkRunner.RunReadWriteAndSaveAsync(
dbContext,
async token =>
{
var entity = new CustomerEntity { Id = Guid.NewGuid(), Name = command.Name };
await customerRepository.AddAsync(entity, token);
return entity.Id;
},
ct);
}
Command Handler (Transactional)
await unitOfWorkRunner.RunReadWriteWithTransactionAsync(
async ct =>
{
await orderRepository.AddAsync(order, ct);
await stockRepository.RemoveByFilterAsync(x => x.ProductId == order.ProductId, cancellationToken: ct);
await dbContext.SaveChangesAsync(ct);
},
DataIsolationLevel.ReadCommitted,
cancellationToken);
Dapper-First Usage
await using IConnectionUnitOfWork uow = await unitOfWorkFactory.OpenReadWriteWithTransactionAsync(
DataIsolationLevel.ReadCommitted,
cancellationToken);
const string sql = "INSERT INTO Customers (Id, Name) VALUES (@Id, @Name);";
await uow.Connection.ExecuteAsync(
sql,
new { Id = Guid.NewGuid(), Name = "John" },
uow.Transaction);
await uow.CommitAsync(cancellationToken);
If you use read replicas, use OpenReadOnlyAsync for query workloads.
Repository Pattern
public interface ICustomerRepository : IBaseRepositoryWithKey<CustomerEntity, Guid>
{
}
public sealed class CustomerRepository(
PosInventoryDbContext dbContext,
IUnitOfWorkSessionAccessor sessionAccessor)
: BaseRepositoryWithKey<CustomerEntity, Guid, PosInventoryDbContext>(
dbContext,
id => x => x.Id == id,
sessionAccessor),
ICustomerRepository
{
}
Project Integration Template
Starter files are available at:
src/OpsFusion.Data/templates/starter/DependencyInjection.cssrc/OpsFusion.Data/templates/starter/appsettings.data.jsonsrc/OpsFusion.Data/templates/starter/SampleCreateCustomerCommandHandler.cssrc/OpsFusion.Data/templates/starter/SampleGetCustomersQueryHandler.cssrc/OpsFusion.Data/templates/starter/SampleDapperRepository.cs
Copy these into a new project and rename namespaces/types.
Integration Checklist
- Add
OpsFusion.Datapackage - Add provider + main/log connection strings
- Register
AddOpsFusionData(configuration) - Register your
DbContext - Register repositories
- Use
IUnitOfWorkRunnerin CQRS handlers - Always save EF changes for writes
- Use transaction mode for atomic writes
Common Mistakes
- Missing provider-specific keys
- Running writes in read-only mode
- Forgetting
SaveChangesAsync - Mixing main and log unit-of-work factories
| 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. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Data.Sqlite (>= 9.0.8)
- Microsoft.EntityFrameworkCore (>= 9.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.8)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 9.0.8)
- Npgsql (>= 9.0.4)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.