# Get By Id Example

## Overview

This document demonstrates the complete **Get By ID flow** using the **GetShippingInfoById** 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 by its unique identifier.

## Complete Flow Architecture

```
GET Request → Controller → Service → Query Handler → RESTClient → External System
                                                                    ↓
Response ← Controller ← Service ← Query Handler ← RESTClient ← External System
```

### **Detailed Flow Breakdown**

```
1. GET Request
   ↓
2. Controller (API Entry Point)
   ↓
3. Service Layer (Business Orchestration)
   ↓
4. Query Handler (RESTClient Integration)
   ├── Internal DTO → Request DTO Mapping
   ├── RESTClient Call to External System
   └── Response DTO → Internal DTO Mapping
   ↓
5. Response (Single Entity)
```

## Step-by-Step Implementation

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

**File**: `ShippingController_GetShippingInfoById.cs`

```csharp
[HttpGet()]
[Route("GetShippingInfoById/{id}")]
[ProducesResponseType(typeof(GetShippingInfoByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetShippingInfoById(string id)
{
    GetShippingInfoByIdParams parameters = new GetShippingInfoByIdParams();
    parameters.Id = id;

    return RunQuerySingleService<GetShippingInfoByIdParams, GetShippingInfoByIdDto>(
                parameters, _processShippingService.GetShippingInfoById);
}
```

**What Happens:**

* **HTTP Method**: `GET /api/Shipping/GetShippingInfoById/{id}`
* **Input**: Shipping ID from URL parameter
* **Parameter Creation**: Creates `GetShippingInfoByIdParams` with the ID
* **Action**: Calls the service layer to process the query
* **Response**: HTTP 200 OK with `GetShippingInfoByIdDto` or 404 Not Found

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

**File**: `ProcessShippingService_GetShippingInfoById.cs`

```csharp
public GetShippingInfoByIdDto GetShippingInfoById(GetShippingInfoByIdParams @params)
{
    return _flexHost.GetFlexiQuery<GetShippingInfoById>().AssignParameters(@params).Fetch();
}
```

**What Happens:**

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

### 3. **Query Handler** - RESTClient Integration

**File**: `GetShippingInfoById.cs`

```csharp
public class GetShippingInfoById : FlexiQueryBridge<Shipping, GetShippingInfoByIdDto>
{
    protected readonly ILogger<GetShippingInfoById> _logger;
    protected GetShippingInfoByIdParams _params;
    protected readonly ShippingRESTClient _restClient;

    public GetShippingInfoById(ILogger<GetShippingInfoById> logger, ShippingRESTClient restClient)
    {
        _logger = logger;
        _restClient = restClient;
    }

    public virtual GetShippingInfoById AssignParameters(GetShippingInfoByIdParams @params)
    {
        _params = @params;
        return this;
    }

    public override GetShippingInfoByIdDto Fetch()
    {
        try
        {
            // Convert internal parameters to Request DTO
            GetShippingInfoByIdRequestDto requestParams = new GetShippingInfoByIdRequestDto
            {
                Id = _params.Id
            };

            // Call external REST service
            var response = _restClient.GetShippingInfoById(requestParams).Result;

            if (response.IsSuccessStatusCode)
            {
                var responseContent = response.Content.ReadAsStringAsync().Result;
                var responseDto = JsonConvert.DeserializeObject<GetShippingInfoByIdResponseDto>(responseContent);
                
                // Convert Response DTO to Internal DTO
                var result = new GetShippingInfoByIdDto
                {
                    Id = responseDto.Id,
                    OrderId = responseDto.OrderId,
                    ShippingAddress = responseDto.ShippingAddress,
                    TrackingNumber = responseDto.TrackingNumber,
                    Status = ParseShippingStatus(responseDto.Status),
                    StatusDescription = responseDto.StatusDescription,
                    TotalWeight = responseDto.TotalWeight,
                    TotalVolume = responseDto.TotalVolume,
                    CreatedDate = responseDto.CreatedDate,
                    LastModifiedDate = responseDto.LastModifiedDate,
                    CreatedBy = responseDto.CreatedBy,
                    LastModifiedBy = responseDto.LastModifiedBy,
                    ShippingItems = responseDto.ShippingItems?.Select(x => new ShippingItemDto
                    {
                        ProductId = x.ProductId,
                        Quantity = x.Quantity,
                        Weight = x.Weight,
                        Volume = x.Volume
                    }).ToList()
                };

                _logger.LogDebug("Shipping info retrieved successfully for ID: {Id}", _params.Id);
                return result;
            }
            else
            {
                _logger.LogWarning("Shipping not found for ID: {Id}. Status: {StatusCode}", _params.Id, response.StatusCode);
                return null;
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error retrieving shipping info for ID: {Id}", _params.Id);
            return null;
        }
    }

    private ShippingStatus ParseShippingStatus(string status)
    {
        return status?.ToLower() switch
        {
            "created" => ShippingStatus.Created,
            "in_transit" => ShippingStatus.InTransit,
            "delivered" => ShippingStatus.Delivered,
            "cancelled" => ShippingStatus.Cancelled,
            _ => ShippingStatus.Unknown
        };
    }
}
```

**What Happens:**

* **Parameter Assignment**: Stores query parameters for use in RESTClient calls
* **Request Mapping**: Converts internal parameters to Request DTO
* **RESTClient Call**: Calls external shipping service via RESTClient
* **Response Mapping**: Converts Response DTO to Internal DTO
* **Error Handling**: Handles success/failure responses from external system
* **Data Transformation**: Applies business logic during mapping

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

**File**: `GetShippingInfoByIdParams.cs`

```csharp
public class GetShippingInfoByIdParams : DtoBridge
{
    public string Id { get; set; }
}
```

**What Happens:**

* **ID Parameter**: Contains the unique identifier for the entity
* **Simple Structure**: Minimal parameters for single entity lookup
* **URL Binding**: ID is automatically bound from URL parameter

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

**File**: `GetShippingInfoByIdDto.cs`

```csharp
public partial class GetShippingInfoByIdDto : DtoBridge 
{
    public string Id { get; set; }
    public string OrderId { get; set; }
    public string ShippingAddress { get; set; }
    public string TrackingNumber { get; set; }
    public ShippingStatus Status { get; set; }
    public string StatusDescription { get; set; }
    public decimal TotalWeight { get; set; }
    public decimal TotalVolume { get; set; }
    public DateTime CreatedDate { get; set; }
    public DateTime? LastModifiedDate { get; set; }
    public string CreatedBy { get; set; }
    public string LastModifiedBy { get; set; }
    public ICollection<ShippingItemDto> ShippingItems { get; set; }
}
```

**What Happens:**

* **Complete Data**: Contains all relevant fields for the entity
* **Detailed Structure**: Includes all properties for full entity display
* **Related Data**: May include related entity information (ShippingItems)
* **Audit Fields**: Includes creation and modification tracking

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

**File**: `GetShippingInfoByIdMapperConfiguration.cs`

```csharp
public partial class GetShippingInfoByIdMapperConfiguration : FlexMapperProfile
{
    public GetShippingInfoByIdMapperConfiguration() : base()
    {
        CreateMap<Shipping, GetShippingInfoByIdDto>()
            .ForMember(d => d.StatusDescription, opt => opt.MapFrom(s => s.Status.ToString()))
            .ForMember(d => d.ShippingItems, opt => opt.MapFrom(s => s.ShippingItems));
    }
}
```

**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 Other Query Operations

### **Get By ID vs Other Query Characteristics**

| Aspect                | Get By ID                  | Get List                     | Get Paged List               |
| --------------------- | -------------------------- | ---------------------------- | ---------------------------- |
| **Return Type**       | `T` (single entity)        | `IEnumerable<T>`             | `FlexiPagedList<T>`          |
| **Input**             | ID from URL                | Query parameters             | Query parameters             |
| **Result Count**      | 0 or 1                     | 0 to many                    | 0 to many (paginated)        |
| **Use Case**          | Single entity details      | Dropdown/lookup              | Data grids/tables            |
| **Performance**       | Very fast (indexed lookup) | Fast for small datasets      | Optimized for large datasets |
| **Controller Method** | `RunQuerySingleService`    | `RunQueryListService`        | `RunQueryPagedService`       |
| **Query Base**        | `FlexiQueryBridge`         | `FlexiQueryEnumerableBridge` | `FlexiQueryPagedListBridge`  |
| **HTTP Status**       | 200 OK or 404 Not Found    | 200 OK                       | 200 OK                       |

### **Get By ID-Specific Features**

1. **Single Entity**: Returns one entity or null
2. **ID-Based Lookup**: Uses unique identifier for fast retrieval
3. **404 Handling**: Returns 404 when entity not found
4. **Complete Data**: Returns all entity details
5. **Fast Performance**: Optimized for indexed lookups

### **Common Use Cases**

* **Entity Details**: Display detailed information about a specific shipping
* **Edit Forms**: Load shipping data for editing
* **View Pages**: Show complete shipping information
* **API Integration**: Retrieve specific shipping for external systems
* **Validation**: Check if shipping exists before operations

## Flow Summary

### **Synchronous Flow (Data Retrieval)**

1. **GET Request** → Controller receives request with ID parameter
2. **Service Processing** → Business orchestration and query resolution
3. **Query Handler** → Database query building and execution
4. **AutoMapper** → Entity-to-DTO transformation
5. **Response** → HTTP 200 OK with entity or 404 Not Found

### **No Asynchronous Flow**

* **No Events**: Query operations don't publish events
* **No Subscribers**: No side effects or event processing
* **Immediate Response**: Data is returned immediately

## Query Building Patterns

### **Basic Query Building**

```csharp
protected override IQueryable<T> Build<T>()
{
    _repoFactory.Init(_params);
    IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>().Where(t => t.Id == _params.Id);
    return query;
}
```

### **Advanced Query Building**

```csharp
protected override IQueryable<T> Build<T>()
{
    _repoFactory.Init(_params);
    IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
    
    // Filter by ID
    query = query.Where(t => t.Id == _params.Id);
    
    // Exclude soft deleted items
    query = query.Where(t => !t.IsSoftDeleted);
    
    // Include related entities if needed
    // query = query.Include(t => t.ShippingItems);
    
    return query;
}
```

### **Error Handling Patterns**

```csharp
public override GetShippingInfoByIdDto Fetch()
{
    var result = Build<Shipping>().SelectTo<GetShippingInfoByIdDto>().FirstOrDefault();
    
    if (result == null)
    {
        _logger.LogWarning("Shipping with ID {ShippingId} not found", _params.Id);
    }
    else
    {
        _logger.LogDebug("Shipping with ID {ShippingId} retrieved successfully", _params.Id);
    }
    
    return result;
}
```

## Performance Considerations

### **Optimization Strategies**

1. **Database Indexing**: Ensure ID field is properly indexed
2. **Selective Fields**: Only return necessary fields in DTO
3. **Related Data**: Use Include() judiciously for related entities
4. **Caching**: Consider caching for frequently accessed entities
5. **Query Optimization**: Use FirstOrDefault() for single entity retrieval

### **When to Use Get By ID vs Other Queries**

| Scenario                | Use Get By ID | Use Get List | Use Get Paged List |
| ----------------------- | ------------- | ------------ | ------------------ |
| **Entity Details Page** | ✅ Yes         | ❌ No         | ❌ No               |
| **Edit Form Loading**   | ✅ Yes         | ❌ No         | ❌ No               |
| **API Integration**     | ✅ Yes         | ❌ No         | ❌ No               |
| **Dropdown Population** | ❌ No          | ✅ Yes        | ❌ No               |
| **Data Grids**          | ❌ No          | ❌ No         | ✅ Yes              |
| **Search Results**      | ❌ No          | ✅ Yes        | ✅ Yes              |
| **Lookup Tables**       | ❌ No          | ✅ Yes        | ❌ No               |

## Error Handling

### **HTTP Status Codes**

* **200 OK**: Entity found and returned successfully
* **404 Not Found**: Entity with specified ID does not exist
* **400 Bad Request**: Invalid ID format or missing ID parameter
* **500 Internal Server Error**: Database or server error

### **Controller Error Handling**

```csharp
[HttpGet()]
[Route("GetShippingInfoById/{id}")]
[ProducesResponseType(typeof(GetShippingInfoByIdDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetShippingInfoById(string id)
{
    if (string.IsNullOrEmpty(id))
    {
        return BadRequest("Shipping ID is required");
    }
    
    GetShippingInfoByIdParams parameters = new GetShippingInfoByIdParams();
    parameters.Id = id;

    return RunQuerySingleService<GetShippingInfoByIdParams, GetShippingInfoByIdDto>(
                parameters, _processShippingService.GetShippingInfoById);
}
```

## Key Benefits

* **Performance**: Very fast retrieval using indexed ID lookup
* **Simplicity**: Simple single entity retrieval
* **Complete Data**: Returns full entity details
* **Type Safety**: Strongly typed DTOs and parameters
* **AutoMapper**: Automatic entity-to-DTO mapping
* **Error Handling**: Built-in 404 handling for missing entities
* **No Side Effects**: Read-only operations
* **Testable**: Each component can be tested independently
* **Maintainable**: Clear separation of concerns

***

**This GetShippingInfoById example demonstrates how FlexBase enables clean, maintainable, and scalable single entity retrieval operations with proper error handling 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/rest-services/get-by-id-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.
