Flowtex.DataAccess
1.0.2
See the version list below for details.
dotnet add package Flowtex.DataAccess --version 1.0.2
NuGet\Install-Package Flowtex.DataAccess -Version 1.0.2
<PackageReference Include="Flowtex.DataAccess" Version="1.0.2" />
<PackageVersion Include="Flowtex.DataAccess" Version="1.0.2" />
<PackageReference Include="Flowtex.DataAccess" />
paket add Flowtex.DataAccess --version 1.0.2
#r "nuget: Flowtex.DataAccess, 1.0.2"
#:package Flowtex.DataAccess@1.0.2
#addin nuget:?package=Flowtex.DataAccess&version=1.0.2
#tool nuget:?package=Flowtex.DataAccess&version=1.0.2
Flowtex.DataAccess
A modern, streamlined data access library for Entity Framework Core that provides a clean alternative to the traditional Repository and Unit of Work patterns. This library simplifies data operations while maintaining performance and testability.
What This Library Does
Flowtex.DataAccess provides a unified interface for data access operations through a set of core abstractions:
IReadStore- Read-only operations with optimized no-tracking queriesIDataStore- Full CRUD operations including reads, writes, and transactionsISaveHandle- Explicit save control for better transaction managementIUpdateBuilder<T>- Fluent API for efficient bulk update operations
The library is built around Entity Framework Core but abstracts away the complexity while preserving all the power and performance benefits.
Why Use This Instead of Repository and Unit of Work Patterns?
Problems with Traditional Patterns
Repository Pattern Issues:
- Creates unnecessary abstraction layers over already-abstracted EF Core
- Often leads to "leaky abstractions" where EF-specific code bleeds through
- Requires extensive mocking for unit testing
- Can hide EF Core's powerful query capabilities behind generic interfaces
- Often results in N+1 query problems due to oversimplified interfaces
Unit of Work Pattern Issues:
- EF Core's
DbContextalready implements Unit of Work - Creates redundant transaction management
- Can lead to confusing nested transaction scenarios
- Adds complexity without meaningful benefits
Flowtex.DataAccess Advantages
1. Direct EF Core Integration
- Leverages EF Core's native capabilities instead of hiding them
- No performance penalties from unnecessary abstractions
- Access to full LINQ query power through
IQueryable<T>
2. Explicit Save Control
ISaveHandleprovides clear control over when data is persisted- Enables batch operations and transaction optimization
- Reduces accidental database round-trips
3. Performance Optimized
- No-tracking queries by default for reads (
IReadStore) - Tracked queries available when needed (
QueryTracked<T>) - Efficient bulk operations with
ExecuteUpdateAsyncandExecuteDeleteAsync
4. Clean Testing
- Interfaces are easily mockable
- No complex repository hierarchies to maintain
- Direct integration with EF Core's in-memory testing capabilities
5. Simplified Architecture
- Single interface for data operations instead of multiple repositories
- Built-in transaction support without separate Unit of Work classes
- Consistent API across all entity types
Examples
Basic Setup
// Implementation example (in your Infrastructure layer)
public class AppDataStore : DataStoreBase
{
private readonly AppDbContext _context;
public AppDataStore(AppDbContext context)
{
_context = context;
}
protected override DbContext Context => _context;
protected override DbSet<T> GetDbSet<T>() => _context.Set<T>();
}
// Dependency injection setup
services.AddScoped<IDataStore, AppDataStore>();
services.AddScoped<IReadStore>(provider => provider.GetService<IDataStore>());
Reading Data
public class ProductService
{
private readonly IReadStore _readStore;
public ProductService(IReadStore readStore)
{
_readStore = readStore;
}
// Simple query
public async Task<List<Product>> GetActiveProductsAsync()
{
return await _readStore.ListAsync<Product>(q =>
q.Where(p => p.IsActive)
.OrderBy(p => p.Name));
}
// Complex query with projection
public async Task<List<ProductSummary>> GetProductSummariesAsync()
{
return await _readStore.ListAsync<Product, ProductSummary>(q =>
q.Where(p => p.IsActive)
.Select(p => new ProductSummary
{
Id = p.Id,
Name = p.Name,
Price = p.Price
}));
}
// Direct IQueryable access for complex scenarios
public IQueryable<Product> GetProductsQuery()
{
return _readStore.Query<Product>();
}
}
Writing Data
public class ProductService
{
private readonly IDataStore _dataStore;
public ProductService(IDataStore dataStore)
{
_dataStore = dataStore;
}
// Adding single entity
public async Task<Product> CreateProductAsync(Product product)
{
var saveHandle = await _dataStore.AddAsync(product);
await saveHandle.SaveAsync();
return product;
}
// Adding multiple entities
public async Task AddProductsAsync(IEnumerable<Product> products)
{
var saveHandle = await _dataStore.AddRangeAsync(products);
await saveHandle.SaveAsync();
}
// Updating entity
public async Task UpdateProductAsync(Product product)
{
var saveHandle = _dataStore.Update(product);
await saveHandle.SaveAsync();
}
// Bulk update
public async Task DiscontinueProductsByCategoryAsync(int categoryId)
{
await _dataStore.ExecuteUpdateAsync<Product>(
p => p.CategoryId == categoryId,
builder => builder.SetConst(p => p.IsDiscontinued, true));
}
// Bulk delete
public async Task DeleteExpiredProductsAsync()
{
await _dataStore.ExecuteDeleteAsync<Product>(
p => p.ExpirationDate < DateTime.UtcNow);
}
}
Transaction Management
public async Task<OrderResult> ProcessOrderAsync(Order order, List<OrderItem> items)
{
return await _dataStore.InTransactionAsync(async dataStore =>
{
// Add order
var orderHandle = await dataStore.AddAsync(order);
await orderHandle.SaveAsync();
// Add order items
foreach (var item in items)
{
item.OrderId = order.Id;
}
var itemsHandle = await dataStore.AddRangeAsync(items);
await itemsHandle.SaveAsync();
// Update inventory
await dataStore.ExecuteUpdateAsync<Product>(
p => items.Select(i => i.ProductId).Contains(p.Id),
builder => builder.Set(p => p.Stock,
p => p.Stock - items.First(i => i.ProductId == p.Id).Quantity));
return new OrderResult { OrderId = order.Id, Success = true };
});
}
Testing Examples
public class ProductServiceTests
{
[Test]
public async Task GetActiveProducts_ReturnsOnlyActiveProducts()
{
// Arrange
var mockReadStore = new Mock<IReadStore>();
var products = new List<Product>
{
new() { Id = 1, Name = "Active Product", IsActive = true },
new() { Id = 2, Name = "Inactive Product", IsActive = false }
}.AsQueryable();
mockReadStore.Setup(x => x.Query<Product>())
.Returns(products);
var service = new ProductService(mockReadStore.Object);
// Act
var result = await service.GetActiveProductsAsync();
// Assert
Assert.That(result.Count, Is.EqualTo(1));
Assert.That(result[0].Name, Is.EqualTo("Active Product"));
}
}
Future Samples
This section will be expanded with additional examples as the library evolves:
Planned Examples
Advanced Query Patterns
- Complex joins and includes
- Dynamic query building
- Specification pattern integration
Performance Optimization
- Query performance analysis
- Batch operation strategies
- Memory-efficient data processing
Integration Patterns
- CQRS implementation
- Event sourcing scenarios
- Microservices data patterns
Advanced Transaction Scenarios
- Distributed transactions
- Saga pattern implementation
- Compensation-based transactions
Testing Strategies
- Integration testing with TestContainers
- Performance testing approaches
- Mocking complex scenarios
Contributing
We welcome contributions! Please see our contributing guidelines for details on how to submit pull requests, report issues, and suggest improvements.
License
This project is licensed under the MIT License - see the LICENSE file for details.
| 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
- Microsoft.EntityFrameworkCore (>= 10.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.