TaskHub.Shared promotes a clean separation of concerns by using repositories to abstract data access.
The TaskHub.Shared.Persistence.Repository module defines several specialized interfaces:
Used for fetching data.
GetAsync(id): Retrieves a single entity by ID.IsExistAsync(id): Checks if an entity exists.Used for persisting data.
AddAsync(item): Adds a new entity.Remove(item): Marks an entity for deletion.Specialized interface for checking if a resource belongs to a specific owner (e.g., UserId).
One of the most powerful features of TaskHub.Shared is Automated Dependency Discovery.
public interface IMyRepository : IReadRepository<Guid, MyEntity>;
public class MyRepository(MyDbContext db) : IMyRepository
{
public async Task<MyEntity?> GetAsync(Guid id, CancellationToken ct) =>
await db.Set<MyEntity>().FindAsync([id], ct);
public async Task<bool> IsExistAsync(Guid id, CancellationToken ct) =>
await db.Set<MyEntity>().AnyAsync(x => x.Id == id, ct);
}
Bootstrap:
In your FullHostBuilder or using AddAppDependencies<IRepository>(), the system will scan your assemblies, find MyRepository, and register it as IMyRepository.
// Inside FullHostBuilder.AppStart()
Builder.Services.AddAppDependencies<IRepository>();
Inject the repository into your command handlers or services.
public class CompleteOrderHandler(IOrderRepository repository, IUnitOfWork unitOfWork)
: ICommandHandler<CompleteOrderCommand, Result>
{
public async Task<Result> HandleAsync(CompleteOrderCommand cmd, CancellationToken ct)
{
var order = await repository.GetAsync(cmd.OrderId, ct);
if (order == null) return ResultFactory.OnFailed(404, "Order not found");
order.Complete();
await unitOfWork.SaveAsync(ct);
return ResultFactory.OnSuccess();
}
}