# 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!** 🚀


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flexbase.in/solution-structure/getting-started/features/crud/get-single-example.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
