> For the complete documentation index, see [llms.txt](https://docs.flexbase.in/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flexbase.in/solution-structure/getting-started/features/crud/get-single-example.md).

# Get Single Example

## Overview

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
```

### **Detailed Flow Breakdown**

```
1. GET Request
   ↓
2. Controller (API Entry Point)
   ↓
3. Service Layer (Business Orchestration)
   ↓
4. Query Handler (Data Retrieval)
   ↓
5. Database (Data Query)
   ↓
6. AutoMapper (Data Transformation)
   ↓
7. Response (Single Entity)
```

## Step-by-Step Implementation

### 1. **API Controller** - The Entry Point

**File**: `ProductsController_GetProductSingle.cs`

```csharp
[HttpGet()]
[Route("GetProductSingle")]
[ProducesResponseType(typeof(GetProductSingleDto), 200)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetProductSingle([FromQuery]GetProductSingleParams parameters)
{
    return RunQuerySingleService<GetProductSingleParams, GetProductSingleDto>(
                parameters, _processProductsService.GetProductSingle);
}
```

**What Happens:**

* **HTTP Method**: `GET /api/Products/GetProductSingle`
* **Input**: Query parameters from URL query string
* **Parameter Binding**: Binds query parameters to `GetProductSingleParams`
* **Action**: Calls the service layer to process the query
* **Response**: HTTP 200 OK with `GetProductSingleDto` or 404 Not Found

### 2. **Service Layer** - Business Orchestration

**File**: `ProcessProductsService_GetProductSingle.cs`

```csharp
public GetProductSingleDto GetProductSingle(GetProductSingleParams @params)
{
    return _flexHost.GetFlexiQuery<GetProductSingle>().AssignParameters(@params).Fetch();
}
```

**What Happens:**

* **Query Resolution**: Gets the FlexiQuery instance for GetProductSingle
* **Parameter Assignment**: Assigns query parameters to the query handler
* **Query Execution**: Calls the Fetch() method to execute the query
* **Result**: Returns single product or null if not found

### 3. **Query Handler** - Data Retrieval

**File**: `GetProductSingle.cs`

```csharp
public class GetProductSingle : FlexiQueryBridge<Product, GetProductSingleDto>
{
    protected readonly ILogger<GetProductSingle> _logger;
    protected GetProductSingleParams _params;
    protected readonly RepoFactory _repoFactory;

    public GetProductSingle(ILogger<GetProductSingle> logger, RepoFactory repoFactory)
    {
        _logger = logger;
        _repoFactory = repoFactory;
    }

    public virtual GetProductSingle AssignParameters(GetProductSingleParams @params)
    {
        _params = @params;
        return this;
    }

    public override GetProductSingleDto Fetch()
    {
        var result = Build<Product>().SelectTo<GetProductSingleDto>().FirstOrDefault();

        return result;
    }

    protected override IQueryable<T> Build<T>()
    {
        _repoFactory.Init(_params);

        IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();

        //Build Your Query Here

        return query;
    }
}
```

**What Happens:**

* **Parameter Assignment**: Stores query parameters for use in query building
* **Query Building**: Creates the database query with custom filtering
* **Data Projection**: Uses AutoMapper to project to DTO
* **Single Result**: Returns first matching entity or null
* **Custom Filtering**: Applies business-specific criteria

### 4. **Query Parameters** - Input DTO

**File**: `GetProductSingleParams.cs`

```csharp
public class GetProductSingleParams : DtoBridge
{
    public string Sku { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }
    public bool? IsActive { get; set; }
    public decimal? MinPrice { get; set; }
    public decimal? MaxPrice { get; set; }
}
```

**What Happens:**

* **Multiple Criteria**: Contains various search parameters
* **Flexible Filtering**: Allows searching by different criteria
* **Query Binding**: Parameters are bound from URL query string
* **Optional Parameters**: All parameters are optional for flexible searching

### 5. **Output DTO** - Data Transfer Object

**File**: `GetProductSingleDto.cs`

```csharp
public partial class GetProductSingleDto : DtoBridge 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public string Sku { get; set; }
    public string CategoryId { get; set; }
    public string CategoryName { get; set; }
    public int StockQuantity { get; set; }
    public bool IsActive { get; set; }
    public DateTime CreatedDate { get; set; }
    public string CreatedBy { get; set; }
}
```

**What Happens:**

* **Complete Data**: Contains all relevant fields for the entity
* **Search Result**: Represents the single entity found by criteria
* **Related Data**: May include related entity information (CategoryName)
* **Audit Fields**: Includes creation and modification tracking

### 6. **AutoMapper Configuration** - Data Transformation

**File**: `GetProductSingleMapperConfiguration.cs`

```csharp
public partial class GetProductSingleMapperConfiguration : FlexMapperProfile
{
    public GetProductSingleMapperConfiguration() : base()
    {
        CreateMap<Product, GetProductSingleDto>()
            .ForMember(d => d.CategoryName, opt => opt.MapFrom(s => s.Category.Name))
            .ForMember(d => d.IsActive, opt => opt.MapFrom(s => !s.IsSoftDeleted));
    }
}
```

**What Happens:**

* **Entity to DTO Mapping**: Maps domain entities to DTOs
* **Custom Mappings**: Handles complex property transformations
* **Related Data**: Maps related entity properties
* **Computed Fields**: Handles calculated properties

## Key Differences from Get By ID

### **Get Single vs Get By ID Characteristics**

| Aspect                | Get Single                     | Get By ID             |
| --------------------- | ------------------------------ | --------------------- |
| **Input**             | Query parameters               | ID from URL           |
| **Search Criteria**   | Multiple criteria              | Single ID             |
| **Use Case**          | Search by business criteria    | Direct ID lookup      |
| **Performance**       | Depends on criteria            | Very fast (indexed)   |
| **Flexibility**       | High (multiple filters)        | Low (single ID)       |
| **URL Pattern**       | `/GetProductSingle?sku=ABC123` | `/GetProductById/123` |
| **Parameter Binding** | `[FromQuery]`                  | URL parameter         |
| **Query Complexity**  | Can be complex                 | Simple ID filter      |

### **Get Single-Specific Features**

1. **Criteria-Based Search**: Uses business criteria instead of ID
2. **Multiple Parameters**: Supports various search filters
3. **Flexible Filtering**: Can search by any combination of criteria
4. **Business Logic**: Applies business-specific search rules
5. **Query Building**: Custom query building in the `Build<T>()` method

### **Common Use Cases**

* **Search by SKU**: Find product by unique SKU code
* **Search by Name**: Find product by partial or exact name match
* **Category Search**: Find a product in a specific category
* **Price Range**: Find product within price range
* **Active Products**: Find active products only
* **Combined Criteria**: Find product matching multiple criteria

## Query Building Patterns

### **Basic Query Building**

```csharp
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;
}
```

### **Advanced Query Building**

```csharp
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;
}
```

### **Error Handling Patterns**

```csharp
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;
}
```

## Common Search Scenarios

### **1. Search by SKU**

```
GET /api/Products/GetProductSingle?sku=ABC123
```

* **Use Case**: Find product by unique SKU code
* **Query**: `WHERE Sku = 'ABC123'`
* **Performance**: Very fast (indexed field)

### **2. Search by Name**

```
GET /api/Products/GetProductSingle?name=iPhone
```

* **Use Case**: Find product by name (partial match)
* **Query**: `WHERE Name LIKE '%iPhone%'`
* **Performance**: Depends on name index

### **3. Search by Category**

```
GET /api/Products/GetProductSingle?categoryId=CAT001
```

* **Use Case**: Find a product in specific category
* **Query**: `WHERE CategoryId = 'CAT001'`
* **Performance**: Fast (indexed field)

### **4. Search by Price Range**

```
GET /api/Products/GetProductSingle?minPrice=100&maxPrice=500
```

* **Use Case**: Find product within price range
* **Query**: `WHERE Price >= 100 AND Price <= 500`
* **Performance**: Depends on price index

### **5. Search by Active Status**

```
GET /api/Products/GetProductSingle?isActive=true
```

* **Use Case**: Find active products only
* **Query**: `WHERE IsActive = true`
* **Performance**: Fast (indexed field)

### **6. Combined Criteria**

```
GET /api/Products/GetProductSingle?categoryId=CAT001&isActive=true&minPrice=100
```

* **Use Case**: Find active product in category with minimum price
* **Query**: `WHERE CategoryId = 'CAT001' AND IsActive = true AND Price >= 100`
* **Performance**: Depends on combined indexes

## Performance Considerations

### **Optimization Strategies**

1. **Database Indexing**: Create indexes on commonly searched fields
2. **Query Optimization**: Use appropriate WHERE clauses
3. **Parameter Validation**: Validate parameters before query execution
4. **Caching**: Consider caching for frequently accessed data
5. **Query Limits**: Consider adding TOP 1 for performance

### **Indexing Recommendations**

```sql
-- 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);
```

## Error Handling

### **HTTP Status Codes**

* **200 OK**: Entity found and returned successfully
* **404 Not Found**: No entity found matching criteria
* **400 Bad Request**: Invalid parameters or missing required criteria
* **500 Internal Server Error**: Database or server error

### **Controller Error Handling**

```csharp
[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);
}
```

## When to Use Get Single vs Other Queries

### **Use Get Single When:**

* **Specific Search**: Need to find one entity by business criteria
* **Unique Lookup**: Search by unique business identifier (SKU, email, etc.)
* **Complex Criteria**: Need to apply multiple search filters
* **Business Logic**: Search requires business-specific rules
* **Single Result**: Expect only one result or want the first match

### **Use Get By ID When:**

* **Direct Lookup**: Have the exact ID of the entity
* **Performance Critical**: Need fastest possible lookup
* **Simple Access**: Direct access to known entity

### **Use Get List When:**

* **Multiple Results**: Need all entities matching criteria
* **Dropdown Data**: Populate dropdowns or lists
* **Small Datasets**: Results are typically small

### **Use Get Paged List When:**

* **Large Datasets**: Results can be large
* **Data Grids**: Display data in paginated tables
* **Performance**: Need to limit result set size

## Key Benefits

* **Flexible Search**: Supports multiple search criteria
* **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!** 🚀
