FBC.DBRepository
0.5.0
dotnet add package FBC.DBRepository --version 0.5.0
NuGet\Install-Package FBC.DBRepository -Version 0.5.0
<PackageReference Include="FBC.DBRepository" Version="0.5.0" />
<PackageVersion Include="FBC.DBRepository" Version="0.5.0" />
<PackageReference Include="FBC.DBRepository" />
paket add FBC.DBRepository --version 0.5.0
#r "nuget: FBC.DBRepository, 0.5.0"
#:package FBC.DBRepository@0.5.0
#addin nuget:?package=FBC.DBRepository&version=0.5.0
#tool nuget:?package=FBC.DBRepository&version=0.5.0
FBC.DBRepository
A lightweight, generic, async-first repository pattern implementation for Entity Framework Core. Supports .NET 8, 9 and 10.
📖 Behaviour notes — the parts that surprise people, and why they are that way. 📋 Changelog — what changed in each version, and what it fixed.
Features
| Feature | Description |
|---|---|
| Generic Repository | EFRepositoryBase<TEntity, TEntityId, TContext> works with any entity and DbContext |
| Async-First | All operations are fully async with CancellationToken support (writes included since 0.5.0) |
| Soft Delete | Built-in soft delete via IEntityHasSoftDeleteFeature (automatically filtered from queries) |
| Restore | Restore soft-deleted entities back to active state via RestoreAsync |
| Audit Tracking | Automatic CreatedDateUTC, UpdatedDateUTC, DeletedDateUTC timestamps |
| User Audit | Optional CreatedBy, UpdatedBy, DeletedBy tracking via interfaces |
| Pagination | Built-in pagination with PaginateResponseModel<T> |
| Entity Validation | Pre-operation async validation hooks via IEntityHasCheckDataFor<TEntity, TId> |
| Role-Based Access Control | Entity-level role checks via IEntityRequiresRole + ICurrentUserProvider |
| Transactions | BeginTransactionAsync / CommitTransactionAsync / RollbackTransactionAsync |
| Bulk Operations | ApplyOperationRange for batch create, update, or delete |
| Bulk Query | GetByIdsAsync for retrieving multiple entities by their IDs in a single query |
| Auto DI Registration | RegisterRepositories() scans assemblies and registers repositories automatically |
| Deterministic Registration | Ambiguous registrations are refused instead of resolved at random |
| Filtered Queryable | GetActiveQueryable() for a raw query that still excludes soft-deleted rows |
Installation
dotnet add package FBC.DBRepository
Quick Start
1. Define Your Entity
public class Product : Entity<int, Product>,
IEntityHasSoftDeleteFeature,
IEntityHasCreatedDate,
IEntityHasUpdatedDate,
IEntityHasCheckDataFor<Product, int>
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
// Soft delete
public bool IsDeleted { get; set; }
// Audit dates
public DateTime CreatedDateUTC { get; set; }
public DateTime? UpdatedDateUTC { get; set; }
// Validation hook
public async Task CheckDataForAsync(EntityOperation operation, bool alsoValidate, IAsyncRepository<Product, int> repository)
{
if (alsoValidate)
{
if (string.IsNullOrWhiteSpace(Name))
throw new ArgumentException("Product name is required.");
if (operation == EntityOperation.Create || operation == EntityOperation.Update)
{
if (await repository.AnyAsync(p => p.Name == Name && !p.Id.Equals(Id)))
throw new ArgumentException("Product name must be unique.");
}
}
}
}
2. Create Your Repository
public class ProductRepository : EFRepositoryBase<Product, int, AppDbContext>
{
public ProductRepository(AppDbContext context) : base(context) { }
}
3. Register in DI
// Program.cs
builder.Services.RegisterRepositories(typeof(ProductRepository).Assembly);
4. Use in Your Services
public class ProductService(IAsyncRepository<Product, int> repo)
{
public async Task<Product> CreateAsync(string name, decimal price)
{
var product = new Product { Name = name, Price = price };
return await repo.ApplyOperation(EntityOperation.Create, product, alsoValidate: true);
}
public async Task<PaginateResponseModel<Product>> GetPagedAsync(int page, int size)
=> await repo.GetListAsync(pageNumber: page, itemsPerPage: size);
public async Task SoftDeleteAsync(int id)
{
var product = await repo.GetByIdAsync(id)
?? throw new KeyNotFoundException();
await repo.ApplyOperation(EntityOperation.Delete, product, alsoValidate: false);
}
public async Task RestoreAsync(int id)
=> await repo.RestoreAsync(id);
}
Core Concepts
Entity Base Class
All entities must inherit from Entity<TId, TEntity>:
public class Order : Entity<long, Order>
{
public string OrderNumber { get; set; } = string.Empty;
public decimal Total { get; set; }
}
The base class provides:
Idproperty (primary key with[Key]attribute)- Automatic
CreatedDateUTCinitialization (if the entity implementsIEntityHasCreatedDate) - Internal validation and audit pipeline via
CheckEntityDataForAsync
Entity Operations
EntityOperation enum controls which operation is being performed:
public enum EntityOperation
{
Create, // Insert a new entity
Update, // Update an existing entity
Delete // Soft delete or permanent delete
}
Use ApplyOperation to perform any CRUD operation:
// Create
await repo.ApplyOperation(EntityOperation.Create, entity, alsoValidate: true);
// Update
await repo.ApplyOperation(EntityOperation.Update, entity, alsoValidate: true);
// Soft delete (sets IsDeleted = true)
await repo.ApplyOperation(EntityOperation.Delete, entity, alsoValidate: false);
// Permanent delete (removes from database)
await repo.ApplyOperation(EntityOperation.Delete, entity, alsoValidate: false, deletePermanent: true);
Features in Detail
Soft Delete
Implement IEntityHasSoftDeleteFeature to enable soft delete:
public class Customer : Entity<int, Customer>, IEntityHasSoftDeleteFeature
{
public string Name { get; set; } = string.Empty;
public bool IsDeleted { get; set; }
}
How it works:
- When you call
ApplyOperation(Delete, entity, ...)withoutdeletePermanent: true, the entity'sIsDeletedis set totrueinstead of being removed from the database. - All queries via
GetAsync,GetListAsync,GetByIdAsync,AnyAsync,CountAsyncautomatically exclude soft-deleted records. - To include deleted records in queries, pass
includeDeletedRecords: true.
// Normal query (excludes deleted)
var activeCustomers = await repo.GetListAsync();
// Include deleted records
var allCustomers = await repo.GetListAsync(includeDeletedRecords: true);
⚠️
GetQueryable()is not filtered — it returns deleted rows too. UseGetActiveQueryable()(0.5.0) when you want the raw queryable without them. Details
Restore (Soft Delete Recovery)
Restore a soft-deleted entity back to active state:
// Restore by ID
var restoredCustomer = await repo.RestoreAsync(customerId);
What RestoreAsync does:
- Finds the entity (including deleted records)
- Checks the roles required for
EntityOperation.Delete(since 0.5.0) - Runs
CheckDataForAsync— only if you passalsoValidate: true(since 0.5.0) - Sets
IsDeletedtofalse - Clears
DeletedDateUTCandDeletedBy(if implemented) - Updates
UpdatedDateUTCandUpdatedBy(if implemented) - Saves the changes
await repo.RestoreAsync(id, alsoValidate: true); // validate before restoring
⚠️ Validation is opt-in on purpose. The row is loaded without any
include, so an entity that validates its child collections would fail every restore. Ask for it when the entity validates only its own columns. Why, and why the role isDeleterather thanUpdate
Audit Tracking
Implement audit interfaces to automatically track timestamps and users:
public class Invoice : Entity<int, Invoice>,
IEntityHasCreatedDate, // CreatedDateUTC set on Create
IEntityHasUpdatedDate, // UpdatedDateUTC set on Update
IEntityHasDeletedDate, // DeletedDateUTC set on Delete
IEntityHasCreatedBy, // CreatedBy set on Create
IEntityHasUpdatedBy, // UpdatedBy set on Update
IEntityHasDeletedBy // DeletedBy set on Delete
{
public string InvoiceNumber { get; set; } = string.Empty;
// Audit timestamps
public DateTime CreatedDateUTC { get; set; }
public DateTime? UpdatedDateUTC { get; set; }
public DateTime? DeletedDateUTC { get; set; }
// Audit users
public string? CreatedBy { get; set; }
public string? UpdatedBy { get; set; }
public string? DeletedBy { get; set; }
}
Automatic behavior per operation:
| Operation | Fields Set |
|---|---|
| Create | CreatedDateUTC = UtcNow, CreatedBy = current user |
| Update | UpdatedDateUTC = UtcNow, UpdatedBy = current user |
| Soft Delete | IsDeleted = true, DeletedDateUTC = UtcNow, DeletedBy = current user |
| Permanent Delete | Only CheckDataForAsync is called (entity is removed) |
User Audit (GetCurrentUser)
To enable user tracking, either override GetCurrentUser() or inject ICurrentUserProvider:
Option 1: Override in repository
public class InvoiceRepository : EFRepositoryBase<Invoice, int, AppDbContext>
{
private readonly IHttpContextAccessor _http;
public InvoiceRepository(AppDbContext context, IHttpContextAccessor http) : base(context)
{
_http = http;
}
protected override string? GetCurrentUser()
=> _http.HttpContext?.User?.FindFirst("UserId")?.Value;
}
Option 2: Use ICurrentUserProvider (recommended)
// Implement the provider
public class CurrentUserProvider(IHttpContextAccessor http) : ICurrentUserProvider
{
public string? GetUserId()
=> http.HttpContext?.User?.FindFirst("UserId")?.Value;
public string? GetUserName()
=> http.HttpContext?.User?.Identity?.Name;
public string[] GetRoles()
=> http.HttpContext?.User?.Claims
.Where(c => c.Type == ClaimTypes.Role)
.Select(c => c.Value)
.ToArray() ?? [];
public bool IsInRole(string role)
=> http.HttpContext?.User?.IsInRole(role) ?? false;
}
// Register in DI
builder.Services.AddScoped<ICurrentUserProvider, CurrentUserProvider>();
// Use in repository (provider is injected via constructor)
public class InvoiceRepository : EFRepositoryBase<Invoice, int, AppDbContext>
{
public InvoiceRepository(AppDbContext context, ICurrentUserProvider provider)
: base(context, provider) { }
}
When ICurrentUserProvider is passed to the repository, GetCurrentUser() automatically returns the provider's GetUserId() value. You can still override GetCurrentUser() for custom logic.
Entity Validation (CheckDataForAsync)
Implement IEntityHasCheckDataFor<TEntity, TId> to add pre-operation validation and data adjustment:
public class Category : Entity<int, Category>,
IEntityHasCheckDataFor<Category, int>
{
public string Name { get; set; } = string.Empty;
public string? Slug { get; set; }
public async Task CheckDataForAsync(EntityOperation operation, bool alsoValidate, IAsyncRepository<Category, int> repository)
{
// Data adjustment (always runs)
Slug = Name.ToLower().Replace(" ", "-");
// Validation (only when alsoValidate is true)
if (alsoValidate)
{
if (string.IsNullOrWhiteSpace(Name))
throw new ArgumentException("Category name is required.");
switch (operation)
{
case EntityOperation.Create:
case EntityOperation.Update:
if (await repository.AnyAsync(c => c.Name == Name && !c.Id.Equals(Id)))
throw new ArgumentException("Category name must be unique.");
break;
case EntityOperation.Delete:
// GetActiveQueryable(), not GetQueryable(): the raw one includes soft-deleted
// products, so a category whose products were all deleted would refuse to go.
var hasProducts = await repository.GetActiveQueryable()
.Where(c => c.Id.Equals(Id))
.SelectMany(c => c.Products)
.AnyAsync();
if (hasProducts)
throw new InvalidOperationException("Cannot delete a category with products.");
break;
}
}
}
}
Key points:
CheckDataForAsyncis called automatically byApplyOperationbefore the database operation.- Use it for both data adjustment (normalizing values, syncing fields) and validation (uniqueness checks, business rules).
- The
repositoryparameter gives you access to the full repository capabilities (AnyAsync,GetAsync,GetListAsync,CountAsync, etc.) for cross-entity checks. - If you need raw
IQueryable<TEntity>access, you can callrepository.GetQueryable(). - Recommended: Prefer using the repository methods (
AnyAsync,GetAsync,GetListAsync) overGetQueryable()— especially if your entity implementsIEntityHasSoftDeleteFeature, because repository methods automatically filter out soft-deleted records from every query. - The
alsoValidateparameter lets you skip validation when you only want data adjustment.
Role-Based Access Control (RBAC)
Enforce entity-level role checks using IEntityRequiresRole + ICurrentUserProvider:
Step 1: Define required roles on your entity
public class Device : Entity<int, Device>, IEntityRequiresRole
{
public string Name { get; set; } = string.Empty;
public string[] GetRequiredRolesFor(EntityOperation operation) => operation switch
{
EntityOperation.Create => ["Edit.Devices"],
EntityOperation.Update => ["Edit.Devices"],
EntityOperation.Delete => ["Edit.Devices", "SysAdmin"],
_ => []
};
}
Step 2: Ensure ICurrentUserProvider is injected into your repository
public class DeviceRepository : EFRepositoryBase<Device, int, AppDbContext>
{
public DeviceRepository(AppDbContext context, ICurrentUserProvider provider)
: base(context, provider) { }
}
How it works:
- Before every
ApplyOperation/ApplyOperationRange, the repository checks if the entity implementsIEntityRequiresRole. - If it does and an
ICurrentUserProvideris available, it callsGetRequiredRolesFor(operation). - If the user has any of the required roles, the operation proceeds.
- If the user has none of the required roles, an
UnauthorizedAccessExceptionis thrown. - If no
ICurrentUserProvideris injected, role checks are skipped (backward compatible).
This provides defense-in-depth: even if an API endpoint accidentally lacks authorization attributes, the repository layer catches unauthorized operations.
⚠️ With no
ICurrentUserProvider, nothing is checked and nothing is said. That default has to stay — seeders and migrations have no user — but it is fail-open. Since 0.5.0CheckRoleRequirementisprotected virtual, so an application can override it and refuse. The override, and why the default is what it is
Pagination
All list queries return PaginateResponseModel<T>:
// Paginated query (page 0, 10 items per page)
var result = await repo.GetListAsync(
predicate: p => p.IsActive,
orderBy: q => q.OrderBy(p => p.Name),
pageNumber: 0,
itemsPerPage: 10,
enableTracking: false
);
// Access pagination metadata
int totalItems = result.TotalFilteredCount;
int totalPages = result.TotalPages;
bool hasNext = result.HasNext;
bool hasPrevious = result.HasPrevious;
IList<Product> items = result.Items;
Special cases:
itemsPerPage = 0returns all records in a single page, andpageNumberis ignored.pageNumberis zero-based (0 = first page).
⚠️ Cap the page size yourself. Nothing here limits it. An unclamped size arriving from a request reads the whole table into memory in one call. Why the library does not cap it
Transactions
Wrap multiple operations in a single atomic unit:
public async Task TransferDeviceAsync(int deviceId, int newGroupId)
{
await repo.BeginTransactionAsync();
try
{
var device = await repo.GetByIdAsync(deviceId)
?? throw new KeyNotFoundException();
device.DeviceGroupId = newGroupId;
await repo.ApplyOperation(EntityOperation.Update, device, alsoValidate: true);
// More operations within the same transaction...
await repo.CommitTransactionAsync();
}
catch
{
await repo.RollbackTransactionAsync();
throw;
}
}
Key points:
CommitTransactionAsyncsaves all changes;RollbackTransactionAsyncdiscards them.- An
InvalidOperationExceptionis thrown if you try to begin a second transaction or commit/rollback without an active transaction.
⚠️ A transaction belongs to the
DbContext, not to the repository. Repositories sharing a context share one transaction, and only the one that began it can commit or roll it back — which is exactly what you want for writing two entities atomically. How that works, and the messages you get when it does not
Bulk Operations
Use ApplyOperationRange for batch operations:
// Batch create
var newProducts = new List<Product> { product1, product2, product3 };
await repo.ApplyOperationRange(EntityOperation.Create, newProducts, alsoValidate: true);
// Batch delete
var toDelete = existingProducts.Where(p => p.IsExpired).ToList();
await repo.ApplyOperationRange(EntityOperation.Delete, toDelete, alsoValidate: false, deletePermanent: true);
Bulk Query (GetByIdsAsync)
Retrieve multiple entities by their IDs in a single database query:
var ids = new[] { 1, 5, 12, 23 };
var products = await repo.GetByIdsAsync(ids, enableTracking: false);
Supports all standard options: include, enableTracking, includeDeletedRecords.
Eager Loading (Include)
Use the include parameter to load related entities:
// Single include
var device = await repo.GetByIdAsync(id,
include: q => q.Include(d => d.DeviceType));
// Multiple includes with ThenInclude
var device = await repo.GetByIdAsync(id,
include: q => q
.Include(d => d.DeviceType)
.Include(d => d.DeviceGroup)
.Include(d => d.Addresses).ThenInclude(a => a.AddrType));
Change Tracking Control
Disable EF Core change tracking for read-only queries to improve performance:
// Read-only (no tracking)
var products = await repo.GetListAsync(enableTracking: false);
// Tracked (needed if you plan to update)
var product = await repo.GetByIdAsync(id, enableTracking: true);
Auto DI Registration
RegisterRepositories scans assemblies and registers all repository implementations:
// Program.cs
builder.Services.RegisterRepositories(typeof(ProductRepository).Assembly);
This registers both:
IAsyncRepository<Product, int>→ProductRepository(base interface)IProductRepository→ProductRepository(custom interface, if exists)
Custom interface example:
public interface IProductRepository : IAsyncRepository<Product, int>
{
Task<IList<Product>> GetExpensiveProductsAsync(decimal minPrice);
}
public class ProductRepository : EFRepositoryBase<Product, int, AppDbContext>, IProductRepository
{
public ProductRepository(AppDbContext context) : base(context) { }
public async Task<IList<Product>> GetExpensiveProductsAsync(decimal minPrice)
{
var result = await GetListAsync(predicate: p => p.Price >= minPrice);
return result.Items;
}
}
Injecting the concrete type. A repository with no interface of its own is only registered as
IAsyncRepository<Product, int>. To inject ProductRepository itself (0.5.0):
builder.Services.RegisterRepositories(includeConcreteTypes: true, typeof(ProductRepository).Assembly);
Also since 0.5.0: two unrelated implementations of one repository interface are refused instead of picked at random, a derived repository wins over its base, open generic repositories are skipped, and an assembly whose types fail to load no longer stops the scan. What each of those means
API Reference
IAsyncRepository<TEntity, TEntityId>
| Method | Description |
|---|---|
GetByIdAsync(id, ...) |
Get a single entity by its primary key |
GetByIdsAsync(ids, ...) |
Get multiple entities by their primary keys in a single query |
GetAsync(predicate, ...) |
Get a single entity matching a predicate |
GetListAsync(predicate, orderBy, include, pageNumber, itemsPerPage, ...) |
Get a paginated list of entities |
GetListAsync(query, ...) |
Get a paginated list from a pre-built IQueryable |
AnyAsync(predicate, ...) |
Check if any entity matches a predicate |
CountAsync(predicate, ...) |
Count entities matching a predicate |
GetQueryable() |
Raw IQueryable — not filtered for soft delete |
GetActiveQueryable() |
The same queryable with the soft-delete filter applied (0.5.0) |
ApplyOperation(operationType, entity, alsoValidate, deletePermanent) |
Create, update, or delete a single entity |
ApplyOperation(…, cancellationToken) |
The same, with a cancellation token (0.5.0) |
ApplyOperationRange(operationType, entities, alsoValidate, deletePermanent) |
Batch create, update, or delete multiple entities |
ApplyOperationRange(…, cancellationToken) |
The same, with a cancellation token (0.5.0) |
RestoreAsync(id) |
Restore a soft-deleted entity — checks roles since (0.5.0) |
RestoreAsync(id, alsoValidate) |
The same, optionally validating first (0.5.0) |
BeginTransactionAsync() |
Begin a database transaction on the repository's DbContext |
CommitTransactionAsync() |
Commit the transaction this repository began |
RollbackTransactionAsync() |
Roll back the transaction this repository began |
EFRepositoryBase adds two members that are not on the interface: CurrentTransaction and
HasActiveTransaction (0.5.0), which report the transaction running on the DbContext whoever
started it.
Entity Marker Interfaces
| Interface | Property | Auto-Set On |
|---|---|---|
IEntityHasSoftDeleteFeature |
bool IsDeleted |
Delete (soft) |
IEntityHasCreatedDate |
DateTime CreatedDateUTC |
Create |
IEntityHasUpdatedDate |
DateTime? UpdatedDateUTC |
Update |
IEntityHasDeletedDate |
DateTime? DeletedDateUTC |
Delete (soft) |
IEntityHasCreatedBy |
string? CreatedBy |
Create |
IEntityHasUpdatedBy |
string? UpdatedBy |
Update |
IEntityHasDeletedBy |
string? DeletedBy |
Delete (soft) |
Behavior Interfaces
| Interface | Purpose |
|---|---|
IEntityHasCheckDataFor<TEntity, TId> |
Pre-operation data adjustment and validation |
IEntityRequiresRole |
Entity-level role-based access control |
ICurrentUserProvider |
Provides current user identity and roles |
IQuery<T> |
Declares GetQueryable(); IAsyncRepository derives from it |
Exceptions
| Type | Thrown when |
|---|---|
UnauthorizedAccessException |
The current user lacks a role the entity requires |
KeyNotFoundException |
RestoreAsync was given an id that does not exist |
InvalidOperationException |
A programming error: transaction misuse, ambiguous registration, restoring an entity without soft delete |
EntityValidationException |
Yours to throw from CheckDataForAsync (0.5.0). Nothing in the library throws it — it exists so applications and endpoints can agree on one type. Why not ValidationException |
PaginateResponseModel<T>
| Property | Type | Description |
|---|---|---|
Items |
IList<T> |
Items for the current page |
PageIndex |
int |
Zero-based current page index |
ItemsPerPage |
int |
Number of items per page |
TotalFilteredCount |
int |
Total matching items across all pages |
TotalPages |
int |
Total number of pages |
HasPrevious |
bool |
True if there is a previous page |
HasNext |
bool |
True if there is a next page |
Breaking Changes
See the changelog for the full history and the reasoning behind each change.
V.0.5.0: behaviour changes — no source changes required
Nothing needs editing to move from 0.4.0, but three things behave differently. Each one turns a silent wrong answer into a visible one, which is the point.
| Change | What used to happen | What happens now |
|---|---|---|
RestoreAsync checks roles |
Any caller could restore, even one who could not have deleted the row | Requires the roles GetRequiredRolesFor(EntityOperation.Delete) returns |
RestoreAsync can validate |
A row that had become invalid while deleted came back, and the database constraint threw | Opt in with alsoValidate: true; the default is unchanged |
| Registration ambiguity | Two unrelated implementations: one won, chosen by Reflection's type order | Throws and names both. Inheritance chains are not ambiguous — the derived type wins |
The first only affects code that calls RestoreAsync on entities implementing
IEntityRequiresRole. The second is opt-in. The third only affects a solution that already had two
unrelated repositories for one interface, where the registration was already arbitrary.
V.0.4.0: IEntityHasCheckDataFor<TEntity, TId>.CheckDataForAsync — parameter change
The third parameter of CheckDataForAsync has been changed from IQueryable<TEntity> to IAsyncRepository<TEntity, TId>.
Before:
public async Task CheckDataForAsync(EntityOperation operation, bool alsoValidate, IQueryable<Product> query)
{
if (await query.AnyAsync(p => p.Name == Name && !p.Id.Equals(Id)))
throw new ArgumentException("Product name must be unique.");
}
After:
public async Task CheckDataForAsync(EntityOperation operation, bool alsoValidate, IAsyncRepository<Product, int> repository)
{
if (await repository.AnyAsync(p => p.Name == Name && !p.Id.Equals(Id)))
throw new ArgumentException("Product name must be unique.");
}
Migration guide:
- Replace
IQueryable<TEntity>withIAsyncRepository<TEntity, TId>in yourCheckDataForAsyncimplementations. - If you were using
query.AnyAsync(...),query.Where(...), etc., prefer using the equivalent repository methods (repository.AnyAsync(...),repository.GetAsync(...),repository.GetListAsync(...)) instead. This is especially recommended if your entity implementsIEntityHasSoftDeleteFeature, because repository methods automatically exclude soft-deleted records from every query. - If you still need raw
IQueryable<TEntity>access, callrepository.GetQueryable().
Requirements
- .NET 8.0, 9.0, or 10.0
- Entity Framework Core (version matched to your .NET target)
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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.11)
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.30)
-
net9.0
- Microsoft.EntityFrameworkCore (>= 9.0.19)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
0.5.0 — RestoreAsync now checks the roles required for Delete, and can validate on request. Automatic registration refuses ambiguity instead of choosing at random, skips open generics, and survives an assembly that fails to load. New: GetActiveQueryable(), EntityValidationException, cancellation tokens on the write path, RegisterRepositories(includeConcreteTypes: true). No source changes are needed to upgrade. Full notes: https://github.com/fatihbahceci/FBC.DBRepository/blob/main/CHANGELOG.md