Files
Anatolii Grynchuk a58b4d9279 docs: switch task management and wiki to gitea, merge to development only for non-release changes
- remove YouTrack references from AGENTS.md
- use Gitea issues for task tracking
- merge to main only when a NuGet release is explicitly requested
- remove IT-642 link from README.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-06 01:29:01 +03:00

205 lines
6.8 KiB
Markdown

# 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 `<Version>` in `.csproj` files**.
At publish time, the TC `HrynCo / HrynCo.EF / publish` build:
1. Writes the current build number into `Directory.Build.props` as `<Version>%build.number%</Version>`.
2. Builds and packs both projects.
3. Pushes the resulting `.nupkg` files to nuget.org.
The build number follows the pattern `1.0.<counter>` (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 {
<<interface>>
+object Id
+DateTimeOffset Created
+DateTimeOffset? Updated
}
class IEntityTId {
<<interface>>
+TId Id
}
class EntityTId {
<<abstract>>
+TId Id
+DateTimeOffset Created
+DateTimeOffset? Updated
}
class Entity {
<<abstract>>
+Guid Id
}
class NamedEntity {
<<abstract>>
+string Name
}
class IUnitOfWork {
<<interface>>
+BeginTransactionAsync() Task~ITransaction~
+GetCurrentTransaction() ITransaction?
+ExecuteInTransactionAsync(action) Task
+ExecuteInTransactionAsync~TResult~(action) Task~TResult~
}
class ITransaction {
<<interface>>
+CommitAsync() Task
+RollbackAsync() Task
+DisposeAsync() ValueTask
}
class PagedResultT {
<<sealed>>
+IReadOnlyList~T~ Items
+int Page
+int PageSize
+int TotalCount
}
}
namespace HrynCo_DAL_EF {
class IEfRepository {
<<interface>>
+ClearChangeTracker()
}
class IEfRepositoryTEntityTId {
<<interface>>
+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 {
<<interface>>
}
class BaseEfRepositoryTDbContextTEntityTEntityId {
<<abstract>>
+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 {
<<abstract>>
#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<TId>` | Base entity contracts |
| `Entity<TId>` / `Entity` | Base entity implementations with auto-generated `Id` |
| `NamedEntity` | Entity with a `Name` property |
| `IRepository<T>` | Generic async repository interface |
| `IUnitOfWork` | Unit of work interface with transaction support |
| `ITransaction` | Async transaction contract |
| `PagedResult<T>` | Pagination result wrapper |
### `HrynCo.DAL.EF`
Entity Framework Core implementations of the abstract contracts.
| Type | Description |
|---|---|
| `BaseRepository<T>` | Base repository with common CRUD operations |
| `BaseEfRepository<T>` | EF Core repository with `DbContext` access |
| `IEfRepository<T>` | 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<Product>
{
public ProductRepository(YourDbContext context) : base(context) { }
}
// 3. Register in DI
services.AddScoped<IRepository<Product>, ProductRepository>();
services.AddScoped<IUnitOfWork, EfUnitOfWork<YourDbContext>>();
```