Carneiro.Core.Repository
7.6.2
dotnet add package Carneiro.Core.Repository --version 7.6.2
NuGet\Install-Package Carneiro.Core.Repository -Version 7.6.2
<PackageReference Include="Carneiro.Core.Repository" Version="7.6.2" />
<PackageVersion Include="Carneiro.Core.Repository" Version="7.6.2" />
<PackageReference Include="Carneiro.Core.Repository" />
paket add Carneiro.Core.Repository --version 7.6.2
#r "nuget: Carneiro.Core.Repository, 7.6.2"
#:package Carneiro.Core.Repository@7.6.2
#addin nuget:?package=Carneiro.Core.Repository&version=7.6.2
#tool nuget:?package=Carneiro.Core.Repository&version=7.6.2
Carneiro.Core.Repository
The EF Core data-access layer for the whole library: a SQL Server–backed IUnitOfWork<TDbContext> that every CRUD, query, count, sum, soft-delete, transaction and stored-procedure operation flows through, plus the DatabaseBuilder/IServiceCollection extensions used to register it. Entities are generic over Carneiro.Core.Entities' IAuditableEntity/IEntity/IEntityDistributed — IDs are either int (IEntity) or Guid (IEntityDistributed).
Registration
services.AddDatabase<AppDbContext>(configuration) // "DatabaseContext" connection string + "Database" config section
.AddUnitOfWork<AppDbContext>()
.AddDataProtection<AppDbContext>(); // optional: persist ASP.NET Data Protection keys in the same database
AddDatabase<TDbContext> registers TDbContext with EF Core (AddDbContext, or AddDbContextPool when DatabaseOptions.UseDbContextPool is true) pre-wired for SQL Server only (UseSqlServerWithOptions), and returns an IDatabaseBuilder for chaining. Overloads accept, in various combinations: a raw connection string, a DatabaseOptions instance (object, Action<DatabaseOptions>, or IConfigurationSection), or a full IConfiguration (expects a DatabaseContext connection-string entry and a Database section bindable to DatabaseOptions) — every overload also accepts an extra Action<DbContextOptionsBuilder> for low-level EF configuration on top of what Carneiro.Core.Repository sets up.
IDatabaseBuilder.AddUnitOfWork<T>() registers, all Scoped, for the given DbContext:
IUnitOfWork<T>→UnitOfWork<T>IStoredProcedureUnitOfWork<T>→StoredProcedureUnitOfWork<T>ITransactionalUnitOfWork<T>→TransactionalUnitOfWork<T>
AddUnitOfWork<TService, TImplementation, TDbContext>() registers a custom IUnitOfWork<TDbContext> implementation/subclass instead (still wiring up the transactional/stored-procedure companions). AddDataProtection<T>() (with optional appName and/or keyLifeTime) calls ASP.NET Core's AddDataProtection().PersistKeysToDbContext<T>(), where T must implement IDataProtectionKeyContext.
IUnitOfWork<TDbContext>
The central interface consumers inject. Generic over TDbContext : DbContext; implements IDisposable/IAsyncDisposable and disposes the underlying DbContext on Dispose/DisposeAsync.
CanConnectAsync()— wrapsDbContext.Database.CanConnectAsync().- Create —
Add<T>,AddRange<T>,AddAsync<T>(sync/async, with/withoutCancellationToken), constrained toT : class, IAuditableEntity. Before staging the entity, these stampIsActive = true,IsDeleted = false,CreateDate = DateTime.UtcNow,UpdateDate = null,DeleteDate = null— audit fields are always set by the unit of work, never left to the caller. - Update —
Update<T>,UpdateRange<T>stampUpdateDate = DateTime.UtcNowthen callDbContext.Update/UpdateRange. - Delete (soft by default) —
Delete<T>(entity)/Delete<T>(entities)soft-delete: stampIsActive = false,IsDeleted = true,DeleteDate = DateTime.UtcNow, thenUpdate/UpdateRangethe row(s). TheDelete<T>(entity, hardDelete: true)/Delete<T>(entities, hardDelete: true)overloads instead callDbContext.Set<T>().Remove/RemoveRange, bypassing the audit stamp entirely. - Querying —
Query<T>()andQuery<T>(expression)returnIQueryable<T>filtered toIsDeleted == false;Query<T>(int id)/Query<T>(int id, expression)add anId == idpredicate on top and additionally requireT : IEntity(so theseint idoverloads only work forint-keyed entities — forIEntityDistributed/Guid-keyed entities use the expression-based overloads instead, e.g.FirstOrDefaultAsync<T>(x => x.Id == guidId)). Soft-delete filtering here is applied in these LINQ helpers, not as an EF Core model-level global query filter — going around the unit of work straight toDbContext.Set<T>()(as the hard-delete path does) bypasses it. - Read helpers —
GetAsync<T>(returnsList<T>),AnyAsync<T>,FirstAsync<T>,FirstOrDefaultAsync<T>,SingleAsync<T>,SingleOrDefaultAsync<T>,LastAsync<T>,LastOrDefaultAsync<T>. Each has overloads for no predicate, anint id, anExpression<Func<T, bool>>, anid+ expression combo, and a trailingCancellationToken— all routed throughQuery<T>(...)(so all respect theIsDeleted == falsefilter), except the three...OrDefaultAsync<T>(int id)shortcuts which queryDbContext.Set<T>()directly with an equivalentId == id && IsDeleted == falsepredicate. - Aggregates —
CountAsync<T>()/CountAsync<T>(expression), andSumAsync<T>overloads fordecimal,decimal?,int,int?projections — all built onQuery<T>(). SaveAsync()/SaveAsync(CancellationToken)—DbContext.SaveChangesAsync(...); nothing above is persisted until this is called.
IBaseRepository / BaseRepository<TDbContext, T>
An optional thin, auto-saving wrapper over IUnitOfWork<TDbContext> for building a typed repository per entity, instead of injecting IUnitOfWork<TDbContext> directly everywhere:
IBaseRepositoryis a non-genericIDisposablemarker;BaseRepository<TDbContext>implements the standard dispose pattern by disposing the wrappedUnitOfWork(protected property).IBaseRepository<T>/BaseRepository<TDbContext, T>(T : class, IEntity—int-keyed entities only) add:GetEntityAsync(int id)→UnitOfWork.SingleOrDefaultAsync<T>(t => t.Id == id)UpdateAsync(T entity)/UpdateAsync(List<T> entities)→Update/UpdateRangethenSaveAsync()AddAsync(T entity)→AddAsyncthenSaveAsync()DeleteAsync(T entity)→Delete(soft delete) thenSaveAsync()
Each method commits immediately (calls
SaveAsync()itself); there's no batching multipleBaseRepositorycalls into oneSaveAsync. Because it's hard-typed toIEntity,Guid-keyed (IEntityDistributed) entities aren't a fit forBaseRepository<TDbContext, T>— useIUnitOfWork<TDbContext>directly for those.
ITransactionalUnitOfWork<TDbContext>
For explicit multi-step transactions with EF Core's retry-aware execution strategy, registered automatically alongside IUnitOfWork by AddUnitOfWork. Every call ultimately:
- Creates an
IExecutionStrategyfromDbContext.Database.CreateExecutionStrategy(). - Opens a new
IServiceScopeand resolves a newTDbContextfrom it (not the caller's ambient context). - Begins an
IDbContextTransactionat the givenIsolationLevel(defaultReadCommittedon every overload that doesn't take one explicitly). - Invokes your delegate, then
db.SaveChangesAsync()andtransaction.CommitAsync(). - On exception: logs it,
transaction.RollbackAsync(), and rethrows.
Two invocation shapes are supported, each with ExecuteAsync/ExecuteWithResultAsync (result-returning) variants and isolation-level/cancellation-token overload combinations:
- Fixed shapes —
Func<IUnitOfWork<TDbContext>, Task/Task<T>>orFunc<IServiceProvider, Task/Task<T>>. ExecuteWithAsync<T>/ExecuteWithResultAsync<T>/ExecuteWithResultAsync<T, Y>— resolves an arbitraryTfrom the new scope (e.g. a custom repository, orIUnitOfWork<TDbContext>itself), so the callback can invoke methods on any service registered in DI.
Because the delegate operates on a freshly scoped TDbContext/IUnitOfWork<TDbContext>, changes made through the caller's own request-scoped unit of work are unaffected by (and not part of) this transaction — only what the callback does with the injected instance is committed or rolled back.
IStoredProcedureUnitOfWork<TDbContext>
For raw stored-procedure calls that don't map to LINQ, registered automatically alongside IUnitOfWork by AddUnitOfWork.
ExecuteStoredProcedureAsync<T>(sql, ..., Func<DbDataReader, Task<T>> action)overloads accept an optionalDictionary<string, object>of named parameters (built into genericDbParameters — name/value only, no direction or explicitDbType), an optionalCommandBehavior(defaultCommandBehavior.Default), and an optionalCancellationToken. They open theDbContext's underlying connection, build aCommandType.StoredProcedurecommand, execute it, hand theDbDataReaderto your projection callback, and close the connection in afinallyblock. These overloads do not wrap the call in an explicit transaction. Every call logs the SQL text and parameter names/values viaILoggerbefore executing — be mindful of sensitive parameter values ending up in logs.ExecuteStoredProcedureAsync<S, T>(sql, IEnumerable<S> sqlParameters, CommandBehavior, Func<DbDataReader, Task<T>>, CancellationToken)(S : DbParameter) is the richer overload: it accepts fully-formed ADO.NET parameters (e.g.SqlParameterwithDirection = Output), runs the whole call inside its ownReadCommittedDbTransaction(commit on success, rollback + rethrow on failure), and returns aStoreProcedureResult<T>combining the projectedResultwith anOutputdictionary of anyOutput/InputOutputparameters.
PaginatedList<T>
A List<T> subclass returned by PaginatedList<T>.CreateAsync(IQueryable<T> source, int pageIndex, int pageSize) (or the page-1 shortcut CreateAsync(source, pageSize)), which runs source.CountAsync() then Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(). Pairs naturally with IUnitOfWork.Query<T>(...), since that already excludes soft-deleted rows. Exposes:
PageIndex,PageSize,Total(item count),TotalPages(Ceiling(Total / PageSize))NextPage/PreviousPage(PageIndex ± 1, not clamped to a valid range)HasPreviousPage(PageIndex > 1),HasNextPage(PageIndex < TotalPages)
StoreProcedureResult<T>
Simple result DTO returned only by the DbParameter-collection stored-procedure overload above: Result (the projected T) and Output (Dictionary<string, object> of output parameter values). Overrides ToString() to dump the output dictionary for logging/debugging.
Entity type configurations
EntityTypeConfiguration<T>(T : class, IEntity) andDistributedEntityTypeConfiguration<T>(T : class, IEntityDistributed) are abstractIEntityTypeConfiguration<T>base classes that callbuilder.HasKey(t => t.Id)for you (intvsGuidkey respectively) and then delegate the rest of the mapping to an abstractConfigureEntity(EntityTypeBuilder<T> builder)you implement — use these instead of implementingIEntityTypeConfiguration<T>directly so every entity's primary key is configured consistently. Neither applies an EF Core global query filter for soft delete; that filtering happens at query time inIUnitOfWork, as noted above.DataProtectionKeyConfigurationconfigures ASP.NET Core Data Protection'sDataProtectionKeyentity:HasKey(t => t.Id)andToTable("DataProtectionKey"), with an optionalAction<EntityTypeBuilder<DataProtectionKey>>constructor hook for further customization. Used together withIDatabaseBuilder.AddDataProtection<T>().
Other extensions
ModelBuilderExtensions.UseUtcDateTimeRead(modelBuilder)— call fromOnModelCreatingto walk every entity property of CLR typeDateTime/DateTime?and install aValueConverterthat stampsDateTimeKind.Utcon read (a no-op on write). SQL Server has no time-zone-aware datetime type, so this keeps values read back from the database consistent with theDateTime.UtcNowaudit stampsUnitOfWorkwrites on create/update/delete.
DatabaseOptions / DatabaseFailureOptions
Bindable from configuration (e.g. the Database section via the AddDatabase(IConfiguration) overload) or built programmatically; consumed by DbContextOptionsBuilderExtensions.UseSqlServerWithOptions, the single place the SQL Server provider is configured (this package does not abstract over other providers):
Timeout(default60seconds) →CommandTimeout.Failure(DatabaseFailureOptions) →EnableRetryOnFailure(maxRetryCount: Retries, maxRetryDelay: TimeSpan.FromSeconds(Seconds)).DatabaseFailureOptions.Retriesdefaults to3,Secondsdefaults to15.EnableSensitiveDataLogging/EnableDetailedErrors(both defaultfalse) → the matching EF CoreDbContextOptionsBuildercalls.QuerySplittingBehavior(nullable) →UseQuerySplittingBehavior, only applied if set.UseRelationalNulls(nullable) →UseRelationalNulls, only applied if set.MinBatchSize/MaxBatchSize(nullable) → the matching SQL Server provider batching options, only applied if set.UseDbContextPool(defaultfalse) → controls whetherAddDatabase<T>callsAddDbContextPool<T>instead ofAddDbContext<T>.
Both classes override ToString() for easy logging of the effective configuration.
| 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
- Carneiro.Core.Entities (>= 7.6.2)
- Microsoft.AspNetCore.DataProtection.EntityFrameworkCore (>= 10.0.12)
- Microsoft.AspNetCore.Identity.EntityFrameworkCore (>= 10.0.12)
- Microsoft.EntityFrameworkCore (>= 10.0.12)
- Microsoft.EntityFrameworkCore.SqlServer (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.12)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Carneiro.Core.Repository:
| Package | Downloads |
|---|---|
|
Carneiro.Core.Tests
Test framework |
|
|
Carneiro.Core.Tests.Abstractions
Test abstractions framework |
|
|
Carneiro.Core.Cache
Simple cache implementation |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 7.6.2 | 111 | 9/28/2026 |
| 7.6.1 | 105 | 9/28/2026 |
| 7.6.0 | 122 | 9/16/2026 |
| 7.5.12 | 119 | 9/16/2026 |
| 7.5.11 | 123 | 9/15/2026 |
| 7.5.10 | 111 | 9/15/2026 |
| 7.5.9 | 114 | 9/14/2026 |
| 7.5.8 | 119 | 9/10/2026 |
| 7.5.7 | 137 | 9/6/2026 |
| 7.5.7-beta.130 | 81 | 9/6/2026 |
| 7.5.6 | 123 | 9/6/2026 |
| 7.5.5 | 129 | 8/30/2026 |
| 7.5.4 | 144 | 8/16/2026 |
| 7.5.3 | 131 | 8/16/2026 |
| 7.5.3-beta.120 | 81 | 8/13/2026 |
| 7.5.2 | 136 | 8/12/2026 |
| 7.5.1 | 153 | 7/16/2026 |
| 7.5.0 | 151 | 7/15/2026 |
| 7.5.0-beta.114 | 83 | 7/15/2026 |
| 7.5.0-beta.113 | 81 | 7/15/2026 |