This document demonstrates the complete Get Single flow using the GetProductSingle feature from the EBusiness application. The flow starts with a GET request to the controller and uses Query projects instead of Handlers in the DomainHandler section for data retrieval. This returns a single entity based on specific criteria (not necessarily by ID).
Complete Flow Architecture
GET Request → Controller → Service → Query Handler → Database → Response
Business Logic: Can implement complex search rules
Single Result: Returns one entity or null
Query Parameters: Easy to use with query strings
Performance: Can be optimized with proper indexing
Type Safety: Strongly typed DTOs and parameters
AutoMapper: Automatic entity-to-DTO mapping
Error Handling: Built-in 404 handling for no results
No Side Effects: Read-only operations
Testable: Each component can be tested independently
Maintainable: Clear separation of concerns
This GetProductSingle example demonstrates how FlexBase enables flexible, criteria-based single entity retrieval operations with proper query building and performance optimization! 🚀
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
// Filter by SKU if provided
if (!string.IsNullOrEmpty(_params.Sku))
{
query = query.Where(p => p.Sku == _params.Sku);
}
return query;
}
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
// Filter by SKU if provided
if (!string.IsNullOrEmpty(_params.Sku))
{
query = query.Where(p => p.Sku == _params.Sku);
}
// Filter by name if provided
if (!string.IsNullOrEmpty(_params.Name))
{
query = query.Where(p => p.Name.Contains(_params.Name));
}
// Filter by category if provided
if (!string.IsNullOrEmpty(_params.CategoryId))
{
query = query.Where(p => p.CategoryId == _params.CategoryId);
}
// Filter by active status if provided
if (_params.IsActive.HasValue)
{
query = query.Where(p => p.IsActive == _params.IsActive.Value);
}
// Filter by price range if provided
if (_params.MinPrice.HasValue)
{
query = query.Where(p => p.Price >= _params.MinPrice.Value);
}
if (_params.MaxPrice.HasValue)
{
query = query.Where(p => p.Price <= _params.MaxPrice.Value);
}
// Exclude soft deleted items
query = query.Where(p => !p.IsSoftDeleted);
// Order by name for consistent results
query = query.OrderBy(p => p.Name);
return query;
}
public override GetProductSingleDto Fetch()
{
var result = Build<Product>().SelectTo<GetProductSingleDto>().FirstOrDefault();
if (result == null)
{
_logger.LogWarning("No product found matching criteria: {Criteria}",
$"Sku: {_params.Sku}, Name: {_params.Name}, CategoryId: {_params.CategoryId}");
}
else
{
_logger.LogDebug("Product found matching criteria: {ProductId}", result.Id);
}
return result;
}
GET /api/Products/GetProductSingle?sku=ABC123
GET /api/Products/GetProductSingle?name=iPhone
GET /api/Products/GetProductSingle?categoryId=CAT001
GET /api/Products/GetProductSingle?minPrice=100&maxPrice=500
GET /api/Products/GetProductSingle?isActive=true
GET /api/Products/GetProductSingle?categoryId=CAT001&isActive=true&minPrice=100
-- Create indexes for common search criteria
CREATE INDEX IX_Product_Sku ON Products (Sku);
CREATE INDEX IX_Product_Name ON Products (Name);
CREATE INDEX IX_Product_CategoryId ON Products (CategoryId);
CREATE INDEX IX_Product_IsActive ON Products (IsActive);
CREATE INDEX IX_Product_Price ON Products (Price);
CREATE INDEX IX_Product_IsSoftDeleted ON Products (IsSoftDeleted);
-- Composite index for common combinations
CREATE INDEX IX_Product_CategoryId_IsActive ON Products (CategoryId, IsActive);
CREATE INDEX IX_Product_IsActive_Price ON Products (IsActive, Price);
[HttpGet()]
[Route("GetProductSingle")]
[ProducesResponseType(typeof(GetProductSingleDto), 200)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetProductSingle([FromQuery]GetProductSingleParams parameters)
{
// Validate that at least one search criteria is provided
if (string.IsNullOrEmpty(parameters.Sku) &&
string.IsNullOrEmpty(parameters.Name) &&
string.IsNullOrEmpty(parameters.CategoryId) &&
!parameters.IsActive.HasValue &&
!parameters.MinPrice.HasValue &&
!parameters.MaxPrice.HasValue)
{
return BadRequest("At least one search criteria must be provided");
}
return RunQuerySingleService<GetProductSingleParams, GetProductSingleDto>(
parameters, _processProductsService.GetProductSingle);
}