# hrynco-ef Reusable Entity Framework Core base library for HrynCo applications. ## Solution The solution (`hrynco-ef.slnx`) contains two projects: | Project | Description | |---|---| | `HrynCo.DAL.Abstract` | Infrastructure-agnostic contracts: entities, repository interfaces, unit of work, transactions, pagination. No EF Core dependency. | | `HrynCo.DAL.EF` | Entity Framework Core implementations of the abstract contracts. Depends on `HrynCo.DAL.Abstract` and EF Core. | The split allows consuming projects to reference only `HrynCo.DAL.Abstract` in domain/application layers, keeping those layers free of EF Core. ## Versioning Versions are managed entirely on the TeamCity side — **do not set `` in `.csproj` files**. At publish time, the TC `HrynCo / HrynCo.EF / publish` build: 1. Writes the current build number into `Directory.Build.props` as `%build.number%`. 2. Builds and packs both projects. 3. Pushes the resulting `.nupkg` files to nuget.org. The build number follows the pattern `1.0.` (e.g. `1.0.6`, `1.0.7`, …). The counter increments automatically on each successful publish run. To release a new version, merge to `main` — the publish build triggers automatically. To bump the major or minor version, update the build number pattern in TC: **HrynCo → HrynCo.EF → publish → Edit Configuration → General → Build number format**. ## Class diagram ```mermaid classDiagram namespace HrynCo_DAL_Abstract { class IEntity { <> +object Id +DateTimeOffset Created +DateTimeOffset? Updated } class IEntityTId { <> +TId Id } class EntityTId { <> +TId Id +DateTimeOffset Created +DateTimeOffset? Updated } class Entity { <> +Guid Id } class NamedEntity { <> +string Name } class IUnitOfWork { <> +BeginTransactionAsync() Task~ITransaction~ +GetCurrentTransaction() ITransaction? +ExecuteInTransactionAsync(action) Task +ExecuteInTransactionAsync~TResult~(action) Task~TResult~ } class ITransaction { <> +CommitAsync() Task +RollbackAsync() Task +DisposeAsync() ValueTask } class PagedResultT { <> +IReadOnlyList~T~ Items +int Page +int PageSize +int TotalCount } } namespace HrynCo_DAL_EF { class IEfRepository { <> +ClearChangeTracker() } class IEfRepositoryTEntityTId { <> +Add(entity) TEntity +AddAsync(entity) Task~TEntity~ +Delete(id) +DeleteAsync(id) Task +Get(filter, orderBy, includes) IQueryable~TEntity~ +GetAll() IQueryable~TEntity~ +GetAllAsync() Task~List~TEntity~~ +GetById(id) TEntity? +GetByIdAsync(id) Task~TEntity?~ +Exists(id) Task~bool~ +UpdateAsync(entity) Task +SaveChangesAsync() Task } class IEfRepositoryTEntity { <> } class BaseEfRepositoryTDbContextTEntityTEntityId { <> +TDbContext DbContext +Add() TEntity +AddAsync() Task~TEntity~ +Delete() +DeleteAsync() Task +Get() IQueryable~TEntity~ +GetAll() IQueryable~TEntity~ +GetAllAsync() Task~List~TEntity~~ +GetById() TEntity? +GetByIdAsync() Task~TEntity?~ +Exists() Task~bool~ +Update() +UpdateAsync() Task +SaveChangesAsync() Task #DoRemove() } class BaseRepositoryTEfRepositoryTDbContextTEntityTEntityId { <> #EfRepository TEfRepository #CreateEfRepository()* TEfRepository } class EfUnitOfWorkTDbContext { +BeginTransactionAsync() Task~ITransaction~ +GetCurrentTransaction() ITransaction? +ExecuteInTransactionAsync() Task } class EfTransactionAdapter { +CommitAsync() Task +RollbackAsync() Task +DisposeAsync() ValueTask } } IEntityTId --|> IEntity : extends EntityTId ..|> IEntityTId : implements Entity --|> EntityTId : extends (TId=Guid) NamedEntity --|> Entity : extends IEfRepositoryTEntityTId --|> IEfRepository : extends IEfRepositoryTEntity --|> IEfRepositoryTEntityTId : extends (TId=int) BaseEfRepositoryTDbContextTEntityTEntityId ..|> IEfRepositoryTEntityTId : implements BaseRepositoryTEfRepositoryTDbContextTEntityTEntityId --> BaseEfRepositoryTDbContextTEntityTEntityId : uses (lazy) EfUnitOfWorkTDbContext ..|> IUnitOfWork : implements EfTransactionAdapter ..|> ITransaction : implements EfUnitOfWorkTDbContext --> EfTransactionAdapter : creates ``` ## Packages ### `HrynCo.DAL.Abstract` Abstract DAL contracts — entities, repository interfaces, unit of work, transactions, and pagination. | Type | Description | |---|---| | `IEntity` / `IEntity` | Base entity contracts | | `Entity` / `Entity` | Base entity implementations with auto-generated `Id` | | `NamedEntity` | Entity with a `Name` property | | `IRepository` | Generic async repository interface | | `IUnitOfWork` | Unit of work interface with transaction support | | `ITransaction` | Async transaction contract | | `PagedResult` | Pagination result wrapper | ### `HrynCo.DAL.EF` Entity Framework Core implementations of the abstract contracts. | Type | Description | |---|---| | `BaseRepository` | Base repository with common CRUD operations | | `BaseEfRepository` | EF Core repository with `DbContext` access | | `IEfRepository` | EF-specific repository interface | | `EfUnitOfWork` | EF Core unit of work implementation | | `EfTransactionAdapter` | Adapts EF transactions to `ITransaction` | ## Usage Reference `HrynCo.DAL.Abstract` for contracts only (e.g. in domain/application layers). Reference `HrynCo.DAL.EF` for the full EF Core implementation (infrastructure layer). ```csharp // 1. Define your entity public class Product : Entity { public string Name { get; set; } = string.Empty; } // 2. Implement your repository public class ProductRepository : BaseEfRepository { public ProductRepository(YourDbContext context) : base(context) { } } // 3. Register in DI services.AddScoped, ProductRepository>(); services.AddScoped>(); ``` ## Related - [IT-642](https://yt.grynco.com.ua/issue/IT-642) — Extract `PagedResult` builder as reusable `IQueryable` extension