diff --git a/Docs/Rest-Parser-Usage.md b/Docs/Rest-Parser-Usage.md
new file mode 100644
index 0000000..8a4eaf0
--- /dev/null
+++ b/Docs/Rest-Parser-Usage.md
@@ -0,0 +1,980 @@
+# REST-Parser Usage Guide
+
+This guide provides detailed information on how to use the REST-Parser library in your .NET applications.
+
+## Table of Contents
+
+- [Installation](#installation)
+- [Quick Start](#quick-start)
+- [Query Syntax Reference](#query-syntax-reference)
+- [Operators](#operators)
+- [Filtering Examples](#filtering-examples)
+- [Sorting Examples](#sorting-examples)
+- [Pagination Examples](#pagination-examples)
+- [Advanced Usage](#advanced-usage)
+- [Exception Handling](#exception-handling)
+- [API Reference](#api-reference)
+- [Best Practices](#best-practices)
+- [Troubleshooting](#troubleshooting)
+
+---
+
+## Installation
+
+### NuGet Package Manager
+```bash
+Install-Package REST-Parser
+```
+
+### .NET CLI
+```bash
+dotnet add package REST-Parser
+```
+
+### Package Reference
+```xml
+
+```
+
+---
+
+## Quick Start
+
+### 1. Define Your Entity
+
+```csharp
+public class Product
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Category { get; set; }
+ public decimal Price { get; set; }
+ public int Stock { get; set; }
+ public DateTime ReleaseDate { get; set; }
+ public bool IsActive { get; set; }
+ public string? Description { get; set; }
+ public double? Rating { get; set; }
+}
+```
+
+### 2. Register the Parser (Dependency Injection)
+
+```csharp
+using REST_Parser.DependencyResolution;
+
+// In Program.cs or Startup.cs
+builder.Services.RegisterRestParser();
+```
+
+### 3. Inject and Use in Your Service/Controller
+
+```csharp
+using REST_Parser;
+using REST_Parser.Models;
+
+public class ProductService
+{
+ private readonly IRestToLinqParser _parser;
+ private readonly AppDbContext _context;
+
+ public ProductService(IRestToLinqParser parser, AppDbContext context)
+ {
+ _parser = parser;
+ _context = context;
+ }
+
+ public RestResult GetProducts(string query)
+ {
+ // Parse and execute the query
+ return _parser.Run(_context.Products, query);
+ }
+}
+```
+
+### 4. Make a Request
+
+```http
+GET /api/products?category=Electronics&price[lt]=1000&$sort_by=price[ASC]&$page=1&$pagesize=20
+```
+
+---
+
+## Query Syntax Reference
+
+### Basic Format
+
+```
+field[operator]=value
+```
+
+### Multiple Conditions
+
+Use `&` to separate conditions:
+
+```
+field1=value1&field2[operator]=value2&field3[operator]=value3
+```
+
+### Special Parameters
+
+- **Sorting**: `$sort_by=field[ASC|DESC]`
+- **Pagination**: `$page=n` and `$pagesize=n`
+
+### Whitespace Handling
+
+Whitespace is automatically trimmed:
+
+```
+field [eq] = value ✅ Valid
+field[eq]=value ✅ Valid
+field = value ✅ Valid (defaults to eq)
+```
+
+---
+
+## Operators
+
+### Comparison Operators
+
+| Operator | Description | Supported Types |
+|----------|-------------|-----------------|
+| `eq` | Equal to (default) | All types |
+| `ne` | Not equal to | All types |
+| `gt` | Greater than | int, double, decimal, DateTime |
+| `ge` | Greater than or equal | int, double, decimal, DateTime |
+| `lt` | Less than | int, double, decimal, DateTime |
+| `le` | Less than or equal | int, double, decimal, DateTime |
+
+### String Operators
+
+| Operator | Description | Example |
+|----------|-------------|---------|
+| `eq` | Exact match (case-sensitive) | `name[eq]=iPhone` |
+| `ne` | Not equal | `name[ne]=Samsung` |
+| `contains` | Contains substring (case-sensitive) | `name[contains]=Pro` |
+
+### Supported Data Types
+
+- ✅ `string`
+- ✅ `int` / `int?`
+- ✅ `double` / `double?`
+- ✅ `decimal` / `decimal?`
+- ✅ `DateTime` / `DateTime?`
+- ✅ `bool` / `bool?`
+- ✅ `Guid` / `Guid?`
+
+---
+
+## Filtering Examples
+
+### String Filtering
+
+```csharp
+// Exact match (default operator)
+"name=iPhone"
+"name[eq]=iPhone"
+
+// Not equal
+"category[ne]=Electronics"
+
+// Contains (case-sensitive)
+"description[contains]=wireless"
+
+// Multiple string conditions
+"category=Electronics&brand=Apple"
+```
+
+**REST Query:**
+```http
+GET /api/products?name[contains]=Pro&category=Electronics
+```
+
+### Numeric Filtering
+
+```csharp
+// Integer
+"stock[gt]=10" // Stock greater than 10
+"stock[le]=100" // Stock less than or equal to 100
+"id=42" // ID equals 42 (default operator)
+
+// Decimal/Double
+"price[lt]=999.99" // Price less than 999.99
+"rating[ge]=4.5" // Rating greater than or equal to 4.5
+```
+
+**REST Query:**
+```http
+GET /api/products?price[gt]=100&price[lt]=1000&stock[gt]=0
+```
+
+### Date Filtering
+
+```csharp
+// Standard date formats
+"releaseDate[gt]=2023-01-01"
+"releaseDate[lt]=2024-12-31"
+
+// Date ranges
+"releaseDate[ge]=2023-01-01&releaseDate[le]=2023-12-31"
+```
+
+**REST Query:**
+```http
+GET /api/products?releaseDate[gt]=2023-06-01&isActive=true
+```
+
+### Boolean Filtering
+
+```csharp
+// Boolean values
+"isActive=true"
+"isActive[eq]=false"
+"isDiscontinued[ne]=true"
+```
+
+**REST Query:**
+```http
+GET /api/products?isActive=true&isFeatured=true
+```
+
+### GUID Filtering
+
+```csharp
+// GUID equality
+"productId[eq]=123e4567-e89b-12d3-a456-426614174000"
+"productId[ne]=123e4567-e89b-12d3-a456-426614174000"
+```
+
+### Nullable Field Filtering
+
+```csharp
+// Works with nullable types
+"rating[ge]=4.0" // double?
+"discount[gt]=10" // decimal?
+"lastPurchaseDate[lt]=2024-01-01" // DateTime?
+```
+
+---
+
+## Sorting Examples
+
+### Single Column Sort
+
+```csharp
+// Ascending (default)
+"$sort_by=name"
+"$sort_by=name[ASC]"
+
+// Descending
+"$sort_by=price[DESC]"
+```
+
+**REST Query:**
+```http
+GET /api/products?$sort_by=price[DESC]
+```
+
+### Multiple Column Sort
+
+```csharp
+// Sort by category ascending, then price descending
+"$sort_by=category[ASC]&$sort_by=price[DESC]"
+
+// Sort by rating descending, then name ascending
+"$sort_by=rating[DESC]&$sort_by=name[ASC]"
+```
+
+**REST Query:**
+```http
+GET /api/products?category=Electronics&$sort_by=brand[ASC]&$sort_by=price[ASC]
+```
+
+### Default Sort Behavior
+
+If no `$sort_by` is specified, results are automatically sorted by `Id` ascending:
+
+```csharp
+// These are equivalent
+""
+"$sort_by=Id[ASC]"
+```
+
+---
+
+## Pagination Examples
+
+### Basic Pagination
+
+```csharp
+// Get page 1 with 20 items
+"$page=1&$pagesize=20"
+
+// Get page 2 with 50 items
+"$page=2&$pagesize=50"
+```
+
+**REST Query:**
+```http
+GET /api/products?$page=2&$pagesize=25
+```
+
+### Pagination with Filtering and Sorting
+
+```csharp
+// Complex query with all features
+"category=Electronics&price[lt]=1000&$sort_by=price[ASC]&$page=1&$pagesize=20"
+```
+
+**REST Query:**
+```http
+GET /api/products?category=Electronics&isActive=true&$sort_by=name[ASC]&$page=1&$pagesize=10
+```
+
+### Default Pagination Behavior
+
+- **Default Page**: 1 (if `$page` is specified without value)
+- **Default Page Size**: 25 (if `$pagesize` is specified without value)
+- **Maximum Page Size**: 1000 (enforced by the parser)
+
+### Pagination Metadata
+
+The `RestResult` includes pagination information:
+
+```csharp
+var result = _parser.Run(_context.Products, query);
+
+Console.WriteLine($"Page: {result.Page}");
+Console.WriteLine($"Page Size: {result.PageSize}");
+Console.WriteLine($"Total Count: {result.TotalCount}");
+Console.WriteLine($"Total Pages: {result.PageCount}");
+
+// Access the data
+var products = result.Data.ToList();
+```
+
+---
+
+## Advanced Usage
+
+### Parse vs Run
+
+#### Using `Parse()` - Just Parse, Don't Execute
+
+```csharp
+// Parse the query without executing it
+var parseResult = _parser.Parse("category=Electronics&price[lt]=1000");
+
+// Inspect what was parsed
+Console.WriteLine($"Number of filters: {parseResult.Expressions.Count}");
+Console.WriteLine($"Number of sorts: {parseResult.SortOrder.Count}");
+Console.WriteLine($"Page: {parseResult.Page}, PageSize: {parseResult.PageSize}");
+
+// Apply manually with custom logic
+IQueryable query = _context.Products;
+
+// Apply your own pre-filters
+query = query.Where(p => p.IsActive);
+
+// Apply parsed expressions
+foreach (var expression in parseResult.Expressions)
+{
+ query = query.Where(expression);
+}
+
+// Apply sorting
+var orderedQuery = query.OrderBy(parseResult.SortOrder[0].Expression);
+// ... etc
+```
+
+#### Using `Run()` - Parse and Execute
+
+```csharp
+// Parse and execute in one call
+var result = _parser.Run(_context.Products, "category=Electronics&price[lt]=1000");
+
+// Data is already filtered, sorted, and paginated
+var products = result.Data.ToList();
+```
+
+### Adding Custom Pre-Filters
+
+```csharp
+// Parse the user's query
+var result = _parser.Parse(userQuery);
+
+// Start with your base query
+IQueryable query = _context.Products
+ .Where(p => p.IsActive) // Always filter active
+ .Where(p => p.TenantId == tenantId); // Tenant isolation
+
+// Apply user's filters
+foreach (var expression in result.Expressions)
+{
+ query = query.Where(expression);
+}
+
+// Continue with sorting and pagination...
+```
+
+### Using with Entity Framework Core
+
+```csharp
+public async Task> GetProductsAsync(string query)
+{
+ var result = _parser.Run(_context.Products.AsNoTracking(), query);
+
+ // Materialize the query
+ result.Data = result.Data.ToList().AsQueryable();
+
+ return result;
+}
+```
+
+### Projection for Performance
+
+```csharp
+var result = _parser.Run(_context.Products, query);
+
+// Project to DTOs to reduce data transfer
+var data = result.Data
+ .Select(p => new ProductDto
+ {
+ Id = p.Id,
+ Name = p.Name,
+ Price = p.Price,
+ Category = p.Category
+ })
+ .ToList();
+```
+
+### Multiple Entity Types
+
+```csharp
+// Register multiple parsers
+builder.Services.RegisterRestParser();
+builder.Services.RegisterRestParser();
+builder.Services.RegisterRestParser();
+
+// Inject specific parsers
+public class MultiService
+{
+ private readonly IRestToLinqParser _productParser;
+ private readonly IRestToLinqParser _customerParser;
+
+ public MultiService(
+ IRestToLinqParser productParser,
+ IRestToLinqParser customerParser)
+ {
+ _productParser = productParser;
+ _customerParser = customerParser;
+ }
+}
+```
+
+---
+
+## Exception Handling
+
+### Exception Types
+
+The library throws three custom exceptions:
+
+```csharp
+using REST_Parser.Exceptions;
+
+try
+{
+ var result = _parser.Run(_context.Products, query);
+ return Ok(result.Data.ToList());
+}
+catch (REST_InvalidFieldnameException ex)
+{
+ // Field doesn't exist on the entity
+ // Example: "invalidField=value"
+ return BadRequest(new { error = "Invalid field", details = ex.Message });
+}
+catch (REST_InvalidOperatorException ex)
+{
+ // Operator not supported for the field type
+ // Example: "name[gt]=test" (gt not valid for strings)
+ return BadRequest(new { error = "Invalid operator", details = ex.Message });
+}
+catch (REST_InvalidValueException ex)
+{
+ // Value cannot be converted to the field's type
+ // Example: "price=notanumber"
+ return BadRequest(new { error = "Invalid value", details = ex.Message });
+}
+catch (ArgumentException ex)
+{
+ // Security limits exceeded
+ // - Query too long (>2000 chars)
+ // - Too many conditions (>50)
+ // - Invalid condition format
+ return BadRequest(new { error = "Invalid query", details = ex.Message });
+}
+```
+
+### Validation in API Controller
+
+```csharp
+[ApiController]
+[Route("api/[controller]")]
+public class ProductsController : ControllerBase
+{
+ private readonly IRestToLinqParser _parser;
+ private readonly AppDbContext _context;
+
+ public ProductsController(IRestToLinqParser parser, AppDbContext context)
+ {
+ _parser = parser;
+ _context = context;
+ }
+
+ [HttpGet]
+ public IActionResult Get([FromQuery] string q)
+ {
+ // Provide default if empty
+ if (string.IsNullOrWhiteSpace(q))
+ {
+ q = "$sort_by=Id&$page=1&$pagesize=20";
+ }
+
+ try
+ {
+ var result = _parser.Run(_context.Products, q);
+
+ return Ok(new
+ {
+ data = result.Data.ToList(),
+ pagination = new
+ {
+ page = result.Page,
+ pageSize = result.PageSize,
+ totalCount = result.TotalCount,
+ totalPages = result.PageCount
+ }
+ });
+ }
+ catch (REST_InvalidFieldnameException ex)
+ {
+ return BadRequest(new { error = "Invalid field name", message = ex.Message });
+ }
+ catch (REST_InvalidOperatorException ex)
+ {
+ return BadRequest(new { error = "Invalid operator", message = ex.Message });
+ }
+ catch (REST_InvalidValueException ex)
+ {
+ return BadRequest(new { error = "Invalid value", message = ex.Message });
+ }
+ catch (ArgumentException ex)
+ {
+ return BadRequest(new { error = "Invalid query format", message = ex.Message });
+ }
+ catch (Exception ex)
+ {
+ // Log the exception
+ return StatusCode(500, new { error = "An error occurred processing your request" });
+ }
+ }
+}
+```
+
+---
+
+## API Reference
+
+### IRestToLinqParser
+
+#### Methods
+
+**`RestResult Parse(string request)`**
+
+Parses a REST query string without executing it.
+
+- **Parameters**:
+ - `request` - The REST query string
+- **Returns**: `RestResult` with parsed expressions and settings
+- **Throws**:
+ - `ArgumentException` - Query exceeds limits
+ - `REST_InvalidFieldnameException` - Invalid field name
+ - `REST_InvalidOperatorException` - Invalid operator
+ - `REST_InvalidValueException` - Invalid value
+
+**`RestResult Run(IQueryable source, string rest)`**
+
+Parses and executes a REST query against a data source.
+
+- **Parameters**:
+ - `source` - The IQueryable data source
+ - `rest` - The REST query string
+- **Returns**: `RestResult` with executed data and metadata
+- **Throws**: Same as `Parse()`
+
+### RestResult
+
+#### Properties
+
+- **`List>> Expressions`** - Filter expressions
+- **`List> SortOrder`** - Sort operations
+- **`IQueryable Data`** - Query result (only populated by `Run()`)
+- **`int Page`** - Current page number (1-based)
+- **`int PageSize`** - Items per page
+- **`int PageCount`** - Total number of pages
+- **`int TotalCount`** - Total items matching filters
+
+### SortBy
+
+#### Properties
+
+- **`Expression> Expression`** - Sort expression
+- **`bool Ascending`** - True for ascending, false for descending
+
+---
+
+## Best Practices
+
+### 1. Always Validate Input
+
+```csharp
+[HttpGet]
+public IActionResult Get([FromQuery] string q = "")
+{
+ if (string.IsNullOrWhiteSpace(q))
+ {
+ q = "$sort_by=Id&$pagesize=20"; // Sensible defaults
+ }
+
+ // Use try-catch for exception handling
+ // ...
+}
+```
+
+### 2. Enforce Maximum Page Size
+
+```csharp
+var result = _parser.Run(_context.Products, query);
+
+// The parser already enforces MAX_PAGE_SIZE (1000)
+// But you can add your own stricter limit
+const int MAX_ALLOWED_PAGE_SIZE = 100;
+if (result.PageSize > MAX_ALLOWED_PAGE_SIZE)
+{
+ return BadRequest($"Page size cannot exceed {MAX_ALLOWED_PAGE_SIZE}");
+}
+```
+
+### 3. Use DTOs for API Responses
+
+```csharp
+var result = _parser.Run(_context.Products, query);
+
+var response = new
+{
+ data = result.Data.Select(p => new ProductDto
+ {
+ Id = p.Id,
+ Name = p.Name,
+ Price = p.Price
+ }).ToList(),
+ page = result.Page,
+ pageSize = result.PageSize,
+ totalCount = result.TotalCount,
+ totalPages = result.PageCount
+};
+
+return Ok(response);
+```
+
+### 4. Add Tenant/User Isolation
+
+```csharp
+// Parse the query
+var parsed = _parser.Parse(query);
+
+// Apply tenant filter first
+IQueryable data = _context.Products
+ .Where(p => p.TenantId == currentTenantId);
+
+// Then apply user's filters
+foreach (var expr in parsed.Expressions)
+{
+ data = data.Where(expr);
+}
+```
+
+### 5. Use AsNoTracking for Read-Only Queries
+
+```csharp
+var result = _parser.Run(
+ _context.Products.AsNoTracking(),
+ query
+);
+```
+
+### 6. Log Failed Queries for Analysis
+
+```csharp
+catch (REST_InvalidFieldnameException ex)
+{
+ _logger.LogWarning(ex, "Invalid field in query: {Query}", query);
+ return BadRequest(new { error = ex.Message });
+}
+```
+
+### 7. Cache Common Queries
+
+```csharp
+// Use distributed cache for common queries
+var cacheKey = $"products:{query}";
+var cached = await _cache.GetStringAsync(cacheKey);
+
+if (cached != null)
+{
+ return JsonSerializer.Deserialize(cached);
+}
+
+var result = _parser.Run(_context.Products, query);
+await _cache.SetStringAsync(cacheKey,
+ JsonSerializer.Serialize(result),
+ new DistributedCacheEntryOptions
+ {
+ AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
+ });
+```
+
+---
+
+## Troubleshooting
+
+### Common Issues
+
+#### Issue: "Query exceeds maximum length"
+
+**Cause**: Query string is longer than 2000 characters.
+
+**Solution**: Simplify your query or contact support if you need a larger limit.
+
+#### Issue: "Query exceeds maximum conditions"
+
+**Cause**: More than 50 filter conditions in the query.
+
+**Solution**: Reduce the number of conditions or use server-side filtering.
+
+#### Issue: "Invalid field name"
+
+**Cause**: Field doesn't exist on the entity.
+
+**Solution**: Check spelling and ensure the property exists:
+```csharp
+public class Product
+{
+ public int Id { get; set; } // Use: id=1
+ public string Name { get; set; } // Use: name=iPhone
+}
+```
+
+#### Issue: "Invalid operator"
+
+**Cause**: Using an operator not supported for that type.
+
+**Solution**:
+- Strings: Only `eq`, `ne`, `contains`
+- Numbers/Dates: `eq`, `ne`, `gt`, `ge`, `lt`, `le`
+- Booleans: Only `eq`, `ne`
+
+#### Issue: "Invalid value"
+
+**Cause**: Value cannot be converted to the field's type.
+
+**Solution**: Ensure value matches the field type:
+```csharp
+price=999.99 // ✅ Correct for decimal
+price=abc // ❌ Invalid
+releaseDate=2023-01-01 // ✅ Correct for DateTime
+releaseDate=notadate // ❌ Invalid
+```
+
+#### Issue: Case Sensitivity
+
+**Cause**: String comparisons are case-sensitive.
+
+**Solution**:
+```csharp
+name=iPhone // Matches "iPhone" but not "iphone"
+name[contains]=pro // Matches "MacBook Pro" but not "MacBook PRO"
+```
+
+If you need case-insensitive search, handle it on the server:
+```csharp
+var result = _parser.Parse(query);
+IQueryable data = _context.Products;
+
+// Apply case-insensitive filter manually
+data = data.Where(p => p.Name.ToLower().Contains(searchTerm.ToLower()));
+
+// Then apply other filters
+foreach (var expr in result.Expressions)
+{
+ data = data.Where(expr);
+}
+```
+
+#### Issue: No Results Returned
+
+**Possible causes**:
+1. Filters are too restrictive
+2. Data doesn't exist
+3. Tenant/user isolation filters
+
+**Debug**:
+```csharp
+var result = _parser.Parse(query);
+Console.WriteLine($"Filters: {result.Expressions.Count}");
+Console.WriteLine($"Sort: {result.SortOrder.Count}");
+
+// Test without filters
+var allData = _context.Products.ToList();
+Console.WriteLine($"Total records: {allData.Count}");
+```
+
+---
+
+## Security Limits
+
+The parser enforces the following limits to prevent abuse:
+
+| Limit | Value | Description |
+|-------|-------|-------------|
+| MAX_QUERY_LENGTH | 2000 | Maximum query string length |
+| MAX_CONDITIONS | 50 | Maximum number of filter conditions |
+| MAX_PAGE_SIZE | 1000 | Maximum page size |
+
+These limits are enforced automatically and will throw `ArgumentException` if exceeded.
+
+---
+
+## Complete Example
+
+Here's a complete working example:
+
+```csharp
+// Entity
+public class Product
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Category { get; set; }
+ public decimal Price { get; set; }
+ public int Stock { get; set; }
+ public DateTime ReleaseDate { get; set; }
+ public bool IsActive { get; set; }
+}
+
+// Startup/Program.cs
+builder.Services.AddDbContext();
+builder.Services.RegisterRestParser();
+
+// Controller
+[ApiController]
+[Route("api/[controller]")]
+public class ProductsController : ControllerBase
+{
+ private readonly IRestToLinqParser _parser;
+ private readonly AppDbContext _context;
+ private readonly ILogger _logger;
+
+ public ProductsController(
+ IRestToLinqParser parser,
+ AppDbContext context,
+ ILogger logger)
+ {
+ _parser = parser;
+ _context = context;
+ _logger = logger;
+ }
+
+ [HttpGet]
+ public IActionResult Get([FromQuery] string q = "")
+ {
+ try
+ {
+ // Default query if none provided
+ if (string.IsNullOrWhiteSpace(q))
+ {
+ q = "$sort_by=Id&$pagesize=20";
+ }
+
+ // Parse and execute
+ var result = _parser.Run(_context.Products.AsNoTracking(), q);
+
+ // Build response
+ return Ok(new
+ {
+ data = result.Data.Select(p => new
+ {
+ p.Id,
+ p.Name,
+ p.Category,
+ p.Price,
+ p.Stock
+ }).ToList(),
+ pagination = new
+ {
+ page = result.Page,
+ pageSize = result.PageSize,
+ totalCount = result.TotalCount,
+ totalPages = result.PageCount
+ }
+ });
+ }
+ catch (REST_InvalidFieldnameException ex)
+ {
+ _logger.LogWarning(ex, "Invalid field in query: {Query}", q);
+ return BadRequest(new { error = "Invalid field name", message = ex.Message });
+ }
+ catch (REST_InvalidOperatorException ex)
+ {
+ _logger.LogWarning(ex, "Invalid operator in query: {Query}", q);
+ return BadRequest(new { error = "Invalid operator", message = ex.Message });
+ }
+ catch (REST_InvalidValueException ex)
+ {
+ _logger.LogWarning(ex, "Invalid value in query: {Query}", q);
+ return BadRequest(new { error = "Invalid value", message = ex.Message });
+ }
+ catch (ArgumentException ex)
+ {
+ _logger.LogWarning(ex, "Invalid query format: {Query}", q);
+ return BadRequest(new { error = "Invalid query", message = ex.Message });
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error processing query: {Query}", q);
+ return StatusCode(500, new { error = "An error occurred processing your request" });
+ }
+ }
+}
+
+// Sample Requests
+// GET /api/products
+// GET /api/products?category=Electronics
+// GET /api/products?price[gt]=100&price[lt]=1000
+// GET /api/products?name[contains]=Pro&isActive=true
+// GET /api/products?category=Electronics&$sort_by=price[ASC]&$page=1&$pagesize=10
+```
+
+---
+
+## Additional Resources
+
+- **GitHub Repository**: https://github.com/BigBadJock/REST-Parser
+- **NuGet Package**: https://www.nuget.org/packages/REST-Parser/
+- **Report Issues**: https://github.com/BigBadJock/REST-Parser/issues
+
+---
+
+**Last Updated**: 2025
+**Library Version**: 1.2.5
+**Target Framework**: .NET 10
diff --git a/Docs/SilverCodeAPI-Usage-Guide.md b/Docs/SilverCodeAPI-Usage-Guide.md
new file mode 100644
index 0000000..ec38834
--- /dev/null
+++ b/Docs/SilverCodeAPI-Usage-Guide.md
@@ -0,0 +1,1493 @@
+# SilverCodeAPI Usage Guide
+
+A .NET 10 library providing infrastructure for data access patterns including Repository, Unit of Work, and Data Service implementations with built-in REST query support.
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Architecture](#architecture)
+- [Installation](#installation)
+- [Quick Start](#quick-start)
+- [Core Concepts](#core-concepts)
+- [Repository Pattern](#repository-pattern)
+- [Data Services](#data-services)
+- [Database Factory & Unit of Work](#database-factory--unit-of-work)
+- [Data Models](#data-models)
+- [REST Query Integration](#rest-query-integration)
+- [Auditing](#auditing)
+- [Complete Examples](#complete-examples)
+- [Best Practices](#best-practices)
+- [Migration Guide](#migration-guide)
+- [Troubleshooting](#troubleshooting)
+
+---
+
+## Overview
+
+SilverCodeAPI is a collection of NuGet packages that provide:
+
+- **Generic Repository Pattern** - CRUD operations with type-safe querying
+- **Data Service Layer** - Business logic abstraction over repositories
+- **REST Query Support** - Integrated with REST-Parser for flexible querying
+- **Multiple ID Types** - Support for `int`, `Guid`, and `string` identifiers
+- **Audit Tracking** - Built-in creation and modification tracking
+- **Unit of Work** - Transaction management across repositories
+- **Read-Only Repositories** - Separate interfaces for read and write operations
+
+### NuGet Packages
+
+| Package | Description |
+|---------|-------------|
+| `Core.Common.Contracts` | Interfaces for repositories, data services, and factories |
+| `Core.Common.DataModels` | Base entity models and DTOs |
+| `Core.Common` | Concrete implementations of repository and service patterns |
+
+---
+
+## Architecture
+
+```
+┌─────────────────────────────────────────────────────┐
+│ Your API Controller │
+└──────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Data Service Layer │
+│ (BaseDataService, BaseDataServiceWithIntId, etc.) │
+└──────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Repository Layer │
+│ (BaseRepository, BaseRepositoryWithIntId, etc.) │
+└──────────────────┬──────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────────────┐
+│ Entity Framework Core DbContext │
+└─────────────────────────────────────────────────────┘
+```
+
+---
+
+## Installation
+
+### Package Manager Console
+
+```powershell
+Install-Package Core.Common.Contracts
+Install-Package Core.Common.DataModels
+Install-Package Core.Common
+Install-Package REST-Parser
+```
+
+### .NET CLI
+
+```bash
+dotnet add package Core.Common.Contracts
+dotnet add package Core.Common.DataModels
+dotnet add package Core.Common
+dotnet add package REST-Parser
+```
+
+### Package References
+
+```xml
+
+
+
+
+
+
+```
+
+---
+
+## Quick Start
+
+### 1. Define Your Entity
+
+Choose a base model based on your ID type:
+
+```csharp
+using Core.Common.DataModels;
+
+// Using integer ID
+public class Product : BaseModelWithIntId
+{
+ public string Name { get; set; }
+ public string Category { get; set; }
+ public decimal Price { get; set; }
+ public int Stock { get; set; }
+}
+
+// Using GUID ID
+public class Order : BaseModelWithGuidId
+{
+ public string OrderNumber { get; set; }
+ public decimal Total { get; set; }
+ public DateTime OrderDate { get; set; }
+}
+
+// Using string ID
+public class UserProfile : BaseModelWithStringId
+{
+ public string Username { get; set; }
+ public string Email { get; set; }
+}
+```
+
+### 2. Create Your DbContext
+
+```csharp
+using Microsoft.EntityFrameworkCore;
+
+public class AppDbContext : DbContext
+{
+ public AppDbContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public DbSet Products { get; set; }
+ public DbSet Orders { get; set; }
+ public DbSet UserProfiles { get; set; }
+}
+```
+
+### 3. Implement Repository
+
+```csharp
+using Core.Common;
+using Core.Common.Contracts;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Logging;
+using REST_Parser;
+
+public interface IProductRepository : IRepositoryWithIntId
+{
+ // Add custom methods if needed
+}
+
+public class ProductRepository : BaseRepositoryWithIntId, IProductRepository
+{
+ public ProductRepository(
+ IDbContextFactory dbContextFactory,
+ IRestToLinqParser parser,
+ ILogger> logger)
+ : base(dbContextFactory, parser, logger)
+ {
+ }
+
+ // Implement custom methods here
+}
+```
+
+### 4. Implement Data Service
+
+```csharp
+using Core.Common;
+using Core.Common.Contracts;
+using Microsoft.Extensions.Logging;
+
+public interface IProductService : IDataServiceWithIntId
+{
+ // Add custom business logic methods
+ Task GetProductByName(string name);
+}
+
+public class ProductService : BaseDataServiceWithIntId, IProductService
+{
+ public ProductService(
+ IRepositoryWithIntId repository,
+ ILogger> logger)
+ : base(repository, logger)
+ {
+ }
+
+ public async Task GetProductByName(string name)
+ {
+ return await repository.GetById(p => p.Name == name);
+ }
+}
+```
+
+### 5. Register Services
+
+```csharp
+using Microsoft.EntityFrameworkCore;
+using REST_Parser.DependencyResolution;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Database
+builder.Services.AddDbContextFactory(options =>
+ options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
+
+// REST Parser
+builder.Services.RegisterRestParser();
+builder.Services.RegisterRestParser();
+builder.Services.RegisterRestParser();
+
+// Repositories
+builder.Services.AddScoped();
+
+// Data Services
+builder.Services.AddScoped();
+
+var app = builder.Build();
+```
+
+### 6. Use in Controller
+
+```csharp
+using Core.Common.DataModels;
+using Microsoft.AspNetCore.Mvc;
+
+[ApiController]
+[Route("api/[controller]")]
+public class ProductsController : ControllerBase
+{
+ private readonly IProductService _productService;
+
+ public ProductsController(IProductService productService)
+ {
+ _productService = productService;
+ }
+
+ // GET: api/products?category=Electronics&price[lt]=1000&$sort_by=price[ASC]&$page=1&$pagesize=20
+ [HttpGet]
+ public IActionResult Search([FromQuery] string q = "")
+ {
+ var result = _productService.Search(q ?? "$sort_by=Id&$pagesize=20");
+
+ return Ok(new
+ {
+ data = result.Data,
+ pagination = result.Pagination
+ });
+ }
+
+ // GET: api/products/5
+ [HttpGet("{id}")]
+ public async Task Get(int id)
+ {
+ var product = await _productService.GetById(id);
+ return product != null ? Ok(product) : NotFound();
+ }
+
+ // POST: api/products
+ [HttpPost]
+ public async Task Create([FromBody] Product product)
+ {
+ var created = await _productService.Add(product);
+ return CreatedAtAction(nameof(Get), new { id = created.Id }, created);
+ }
+
+ // PUT: api/products/5
+ [HttpPut("{id}")]
+ public async Task Update(int id, [FromBody] Product product)
+ {
+ if (id != product.Id)
+ return BadRequest();
+
+ var updated = await _productService.Update(product);
+ return Ok(updated);
+ }
+
+ // DELETE: api/products/5
+ [HttpDelete("{id}")]
+ public async Task Delete(int id)
+ {
+ var success = await _productService.Delete(p => p.Id == id);
+ return success ? NoContent() : NotFound();
+ }
+}
+```
+
+---
+
+## Core Concepts
+
+### ID Type Variants
+
+SilverCodeAPI provides three sets of base classes for different ID types:
+
+#### Integer IDs
+
+```csharp
+// Base Model
+public class Product : BaseModelWithIntId { }
+
+// Repository Interface
+public interface IProductRepository : IRepositoryWithIntId { }
+
+// Repository Implementation
+public class ProductRepository : BaseRepositoryWithIntId { }
+
+// Data Service Interface
+public interface IProductService : IDataServiceWithIntId { }
+
+// Data Service Implementation
+public class ProductService : BaseDataServiceWithIntId { }
+```
+
+#### GUID IDs
+
+```csharp
+// Base Model
+public class Order : BaseModelWithGuidId { }
+
+// Repository Interface
+public interface IOrderRepository : IRepositoryWithGuidId { }
+
+// Repository Implementation
+public class OrderRepository : BaseRepositoryWithGuidId { }
+
+// Data Service Interface
+public interface IOrderService : IDataServiceWithGuidId { }
+
+// Data Service Implementation
+public class OrderService : BaseDataServiceWithGuidId { }
+```
+
+#### String IDs
+
+```csharp
+// Base Model
+public class UserProfile : BaseModelWithStringId { }
+
+// Repository Interface
+public interface IUserProfileRepository : IRepositoryWithStringId { }
+
+// Repository Implementation
+public class UserProfileRepository : BaseRepositoryWithStringId { }
+
+// Data Service Interface
+public interface IUserProfileService : IDataServiceWithStringId { }
+
+// Data Service Implementation
+public class UserProfileService : BaseDataServiceWithStringId { }
+```
+
+### Read-Only Operations
+
+Separate read-only interfaces for query-only scenarios:
+
+```csharp
+// Read-only repository
+public interface IProductReadOnlyRepository : IReadRepositoryWithIntId { }
+
+public class ProductReadOnlyRepository : BaseReadRepositoryWithIntId, IProductReadOnlyRepository
+{
+ public ProductReadOnlyRepository(
+ IDbContextFactory dbContextFactory,
+ IRestToLinqParser parser,
+ ILogger> logger)
+ : base(dbContextFactory, parser, logger)
+ {
+ }
+}
+```
+
+---
+
+## Repository Pattern
+
+### Available Methods
+
+#### Read Operations
+
+```csharp
+// Get all records
+IQueryable products = repository.GetAll();
+
+// Get with REST query
+ApiResult result = repository.GetAll("category=Electronics&price[lt]=1000");
+
+// Get by ID
+Product product = await repository.GetById(5);
+
+// Get by ID with child entities
+Product productWithChildren = await repository.GetById(5, includeChildren: true);
+
+// Get by condition
+Product product = await repository.GetById(p => p.Name == "iPhone");
+
+// Get multiple by condition
+IEnumerable products = repository.GetMany(p => p.Category == "Electronics");
+```
+
+#### Write Operations
+
+```csharp
+// Add entity
+Product newProduct = await repository.Add(product);
+
+// Add without immediate commit
+Product newProduct = await repository.Add(product, commit: false);
+await repository.Commit(); // Commit later
+
+// Batch add with progress reporting
+IProgress progress = new Progress(report =>
+{
+ Console.WriteLine($"{report.Message}: {report.CurrentProgress}/{report.TotalProgress}");
+});
+
+await repository.AddBatch(products, batchSize: 100, progress);
+
+// Update entity
+Product updated = await repository.Update(product);
+
+// Delete by condition
+bool deleted = await repository.Delete(p => p.Id == 5);
+
+// Delete entity
+bool deleted = await repository.Delete(product);
+
+// Manual commit
+await repository.Commit();
+```
+
+### Custom Repository Methods
+
+```csharp
+public interface IProductRepository : IRepositoryWithIntId
+{
+ Task> GetLowStockProducts(int threshold);
+ Task> GetProductsByCategory(string category);
+ Task GetAveragePriceByCategory(string category);
+}
+
+public class ProductRepository : BaseRepositoryWithIntId, IProductRepository
+{
+ public ProductRepository(
+ IDbContextFactory dbContextFactory,
+ IRestToLinqParser parser,
+ ILogger> logger)
+ : base(dbContextFactory, parser, logger)
+ {
+ }
+
+ public async Task> GetLowStockProducts(int threshold)
+ {
+ return await Task.FromResult(
+ dbset.Where(p => p.Stock < threshold && !p.IsDeleted)
+ );
+ }
+
+ public async Task> GetProductsByCategory(string category)
+ {
+ return await Task.FromResult(
+ dbset.Where(p => p.Category == category && !p.IsDeleted)
+ );
+ }
+
+ public async Task GetAveragePriceByCategory(string category)
+ {
+ return await dbset
+ .Where(p => p.Category == category && !p.IsDeleted)
+ .AverageAsync(p => p.Price);
+ }
+}
+```
+
+---
+
+## Data Services
+
+### Available Methods
+
+```csharp
+public interface IDataService
+{
+ Task Add(T model);
+ Task Update(T model);
+ Task Delete(Expression> where);
+ ApiResult Search(string restQuery);
+}
+
+// With ID-specific methods
+public interface IDataServiceWithIntId
+{
+ // All IDataService methods plus:
+ Task GetById(int id);
+}
+```
+
+### Custom Data Service Methods
+
+```csharp
+public interface IProductService : IDataServiceWithIntId
+{
+ // Business logic methods
+ Task AdjustStock(int productId, int quantity);
+ Task> GetFeaturedProducts();
+ Task DiscontinueProduct(int productId);
+ Task CalculateTotalInventoryValue();
+}
+
+public class ProductService : BaseDataServiceWithIntId, IProductService
+{
+ private new readonly IProductRepository repository;
+
+ public ProductService(
+ IRepositoryWithIntId repository,
+ ILogger> logger)
+ : base(repository, logger)
+ {
+ this.repository = (IProductRepository)repository;
+ }
+
+ public async Task AdjustStock(int productId, int quantity)
+ {
+ try
+ {
+ logger.LogInformation($"Adjusting stock for product {productId} by {quantity}");
+
+ var product = await repository.GetById(productId);
+ if (product == null)
+ return false;
+
+ product.Stock += quantity;
+
+ if (product.Stock < 0)
+ throw new InvalidOperationException("Insufficient stock");
+
+ await repository.Update(product);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, $"Error adjusting stock for product {productId}");
+ throw;
+ }
+ }
+
+ public async Task> GetFeaturedProducts()
+ {
+ return await repository.GetLowStockProducts(10);
+ }
+
+ public async Task DiscontinueProduct(int productId)
+ {
+ var product = await repository.GetById(productId);
+ if (product == null)
+ return false;
+
+ product.IsDeleted = true;
+ await repository.Update(product);
+ return true;
+ }
+
+ public async Task CalculateTotalInventoryValue()
+ {
+ var products = repository.GetAll();
+ return await products
+ .Where(p => !p.IsDeleted)
+ .SumAsync(p => p.Price * p.Stock);
+ }
+}
+```
+
+---
+
+## Database Factory & Unit of Work
+
+### Database Factory
+
+Implement `IDatabaseFactory` for DbContext management:
+
+```csharp
+using Core.Common;
+using Core.Common.Contracts;
+using Microsoft.EntityFrameworkCore;
+
+public interface IAppDatabaseFactory : IDatabaseFactory
+{
+}
+
+public class AppDatabaseFactory : BaseDatabaseFactory, IAppDatabaseFactory
+{
+ public AppDatabaseFactory(IDbContextFactory contextFactory)
+ : base(contextFactory)
+ {
+ }
+}
+
+// Register in DI
+builder.Services.AddScoped();
+```
+
+### Unit of Work
+
+Implement `IUnitOfWork` for transaction management:
+
+```csharp
+using Core.Common.Contracts;
+using Microsoft.EntityFrameworkCore;
+
+public interface IAppUnitOfWork : IUnitOfWork
+{
+ IProductRepository Products { get; }
+ IOrderRepository Orders { get; }
+}
+
+public class AppUnitOfWork : IAppUnitOfWork
+{
+ private readonly AppDbContext _context;
+ private readonly IProductRepository _productRepository;
+ private readonly IOrderRepository _orderRepository;
+
+ public AppUnitOfWork(
+ IAppDatabaseFactory databaseFactory,
+ IProductRepository productRepository,
+ IOrderRepository orderRepository)
+ {
+ _context = databaseFactory.Get();
+ _productRepository = productRepository;
+ _orderRepository = orderRepository;
+ }
+
+ public IProductRepository Products => _productRepository;
+ public IOrderRepository Orders => _orderRepository;
+
+ public async Task CommitAsync()
+ {
+ return await _context.SaveChangesAsync();
+ }
+
+ public void Dispose()
+ {
+ _context?.Dispose();
+ }
+}
+
+// Usage
+public class OrderService
+{
+ private readonly IAppUnitOfWork _unitOfWork;
+
+ public OrderService(IAppUnitOfWork unitOfWork)
+ {
+ _unitOfWork = unitOfWork;
+ }
+
+ public async Task CreateOrderWithStockUpdate(Order order, int productId, int quantity)
+ {
+ // Add order
+ var createdOrder = await _unitOfWork.Orders.Add(order, commit: false);
+
+ // Update product stock
+ var product = await _unitOfWork.Products.GetById(productId);
+ product.Stock -= quantity;
+ await _unitOfWork.Products.Update(product, commit: false);
+
+ // Commit both changes together
+ await _unitOfWork.CommitAsync();
+
+ return createdOrder;
+ }
+}
+```
+
+---
+
+## Data Models
+
+### Base Model Properties
+
+All base models include:
+
+```csharp
+public abstract class BaseModel
+{
+ public bool IsDeleted { get; set; } // Soft delete flag
+ public DateTime Created { get; set; } // Creation timestamp
+ public string CreatedBy { get; set; } // Creator identifier
+ public DateTime? LastUpdated { get; set; } // Last update timestamp
+ public string LastUpdatedBy { get; set; } // Last updater identifier
+}
+```
+
+### ID-Specific Models
+
+```csharp
+// Integer ID
+public class BaseModelWithIntId : BaseModel, IModelWithId
+{
+ public int Id { get; set; }
+}
+
+// GUID ID
+public class BaseModelWithGuidId : BaseModel, IModelWithGuid
+{
+ public Guid Id { get; set; } = Guid.NewGuid();
+}
+
+// String ID
+public class BaseModelWithStringId : BaseModel, IModelWithStringId
+{
+ public string Id { get; set; }
+}
+```
+
+### Lookup Models
+
+For simple lookup/reference data:
+
+```csharp
+public class BaseLookupModel : BaseModelWithIntId, ILookupModel
+{
+ public string Code { get; set; }
+ public string Description { get; set; }
+}
+
+// Example usage
+public class Category : BaseLookupModel
+{
+ // Inherits: Id, Code, Description, IsDeleted, Created, etc.
+}
+```
+
+### Custom Models
+
+```csharp
+public class Product : BaseModelWithIntId
+{
+ public string Name { get; set; }
+ public string SKU { get; set; }
+ public decimal Price { get; set; }
+ public int CategoryId { get; set; }
+
+ // Navigation property
+ public Category Category { get; set; }
+}
+
+public class Order : BaseModelWithGuidId
+{
+ public string OrderNumber { get; set; }
+ public DateTime OrderDate { get; set; }
+ public decimal Total { get; set; }
+
+ // Collection navigation
+ public List Items { get; set; }
+}
+
+public class OrderItem : BaseModelWithIntId
+{
+ public Guid OrderId { get; set; }
+ public int ProductId { get; set; }
+ public int Quantity { get; set; }
+ public decimal UnitPrice { get; set; }
+
+ // Navigation properties
+ public Order Order { get; set; }
+ public Product Product { get; set; }
+}
+```
+
+---
+
+## REST Query Integration
+
+SilverCodeAPI integrates seamlessly with REST-Parser. See [Rest-Parser-Usage.md](Rest-Parser-Usage.md) for full query syntax.
+
+### Basic Queries
+
+```csharp
+// Simple search
+var result = productService.Search("category=Electronics");
+
+// With operators
+var result = productService.Search("price[lt]=1000&stock[gt]=0");
+
+// With sorting
+var result = productService.Search("category=Electronics&$sort_by=price[ASC]");
+
+// With pagination
+var result = productService.Search("isActive=true&$page=1&$pagesize=20");
+```
+
+### ApiResult Structure
+
+```csharp
+public class ApiResult
+{
+ public IEnumerable Data { get; set; }
+ public Pagination? Pagination { get; set; }
+}
+
+public class Pagination
+{
+ public int PageNumber { get; set; }
+ public int PageSize { get; set; }
+ public int PageCount { get; set; }
+ public int TotalCount { get; set; }
+}
+```
+
+### Controller Example
+
+```csharp
+[HttpGet]
+public IActionResult Search([FromQuery] string q = "")
+{
+ try
+ {
+ var result = _productService.Search(q ?? "$pagesize=20");
+
+ var response = new
+ {
+ data = result.Data,
+ page = result.Pagination?.PageNumber ?? 1,
+ pageSize = result.Pagination?.PageSize ?? result.Data.Count(),
+ totalCount = result.Pagination?.TotalCount ?? result.Data.Count(),
+ totalPages = result.Pagination?.PageCount ?? 1
+ };
+
+ return Ok(response);
+ }
+ catch (REST_InvalidFieldnameException ex)
+ {
+ return BadRequest(new { error = "Invalid field", message = ex.Message });
+ }
+ catch (REST_InvalidOperatorException ex)
+ {
+ return BadRequest(new { error = "Invalid operator", message = ex.Message });
+ }
+ catch (REST_InvalidValueException ex)
+ {
+ return BadRequest(new { error = "Invalid value", message = ex.Message });
+ }
+}
+```
+
+### Query Examples
+
+```http
+# Get all electronics under $1000, sorted by price
+GET /api/products?category=Electronics&price[lt]=1000&$sort_by=price[ASC]
+
+# Get page 2 of active products with stock
+GET /api/products?isActive=true&stock[gt]=0&$page=2&$pagesize=25
+
+# Search by name containing "Pro"
+GET /api/products?name[contains]=Pro&$sort_by=price[DESC]
+
+# Complex query with multiple conditions
+GET /api/products?category=Electronics&price[ge]=100&price[le]=500&stock[gt]=5&$sort_by=name[ASC]&$page=1&$pagesize=10
+```
+
+---
+
+## Auditing
+
+### Built-in Audit Fields
+
+All entities automatically track:
+
+```csharp
+public class Product : BaseModelWithIntId
+{
+ // Your properties
+ public string Name { get; set; }
+
+ // Inherited audit properties:
+ // - DateTime Created
+ // - string CreatedBy
+ // - DateTime? LastUpdated
+ // - string LastUpdatedBy
+ // - bool IsDeleted
+}
+```
+
+### Custom Auditor
+
+Implement `IAuditor` to populate audit fields:
+
+```csharp
+using Core.Common;
+using Core.Common.Contracts;
+using Microsoft.AspNetCore.Http;
+
+public class UserAuditor : BaseAuditor
+{
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public UserAuditor(IHttpContextAccessor httpContextAccessor)
+ {
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ public override string GetCurrentUser()
+ {
+ return _httpContextAccessor.HttpContext?.User?.Identity?.Name ?? "System";
+ }
+}
+
+// Register in DI
+builder.Services.AddHttpContextAccessor();
+builder.Services.AddScoped();
+```
+
+### Using Auditor in Repository
+
+```csharp
+public class ProductRepository : BaseRepositoryWithIntId
+{
+ private readonly IAuditor _auditor;
+
+ public ProductRepository(
+ IDbContextFactory dbContextFactory,
+ IRestToLinqParser parser,
+ ILogger> logger,
+ IAuditor auditor)
+ : base(dbContextFactory, parser, logger)
+ {
+ _auditor = auditor;
+ }
+
+ public override async Task Add(Product entity, bool commit = true)
+ {
+ entity.CreatedBy = _auditor.GetCurrentUser();
+ entity.Created = DateTime.UtcNow;
+
+ return await base.Add(entity, commit);
+ }
+
+ public override async Task Update(Product entity, bool commit = true)
+ {
+ entity.LastUpdatedBy = _auditor.GetCurrentUser();
+ entity.LastUpdated = DateTime.UtcNow;
+
+ return await base.Update(entity, commit);
+ }
+}
+```
+
+### Soft Delete
+
+```csharp
+// Instead of hard delete, use soft delete
+public async Task SoftDelete(int productId)
+{
+ var product = await repository.GetById(productId);
+ if (product == null)
+ return false;
+
+ product.IsDeleted = true;
+ product.LastUpdatedBy = _auditor.GetCurrentUser();
+ product.LastUpdated = DateTime.UtcNow;
+
+ await repository.Update(product);
+ return true;
+}
+
+// Filter out deleted items in queries
+public IQueryable GetActiveProducts()
+{
+ return repository.GetAll().Where(p => !p.IsDeleted);
+}
+```
+
+---
+
+## Complete Examples
+
+### E-Commerce Product Management
+
+```csharp
+// Models
+public class Product : BaseModelWithIntId
+{
+ public string SKU { get; set; }
+ public string Name { get; set; }
+ public string Description { get; set; }
+ public decimal Price { get; set; }
+ public int Stock { get; set; }
+ public int CategoryId { get; set; }
+ public bool IsFeatured { get; set; }
+
+ public Category Category { get; set; }
+}
+
+public class Category : BaseLookupModel
+{
+ public List Products { get; set; }
+}
+
+// Repository
+public interface IProductRepository : IRepositoryWithIntId
+{
+ Task> GetFeaturedProducts();
+ Task> GetLowStockProducts(int threshold);
+}
+
+public class ProductRepository : BaseRepositoryWithIntId, IProductRepository
+{
+ public ProductRepository(
+ IDbContextFactory dbContextFactory,
+ IRestToLinqParser parser,
+ ILogger> logger)
+ : base(dbContextFactory, parser, logger)
+ {
+ }
+
+ public async Task> GetFeaturedProducts()
+ {
+ return await Task.FromResult(
+ dbset.Where(p => p.IsFeatured && !p.IsDeleted)
+ .Include(p => p.Category)
+ );
+ }
+
+ public async Task> GetLowStockProducts(int threshold)
+ {
+ return await Task.FromResult(
+ dbset.Where(p => p.Stock < threshold && !p.IsDeleted)
+ );
+ }
+}
+
+// Service
+public interface IProductService : IDataServiceWithIntId
+{
+ Task> GetFeaturedProducts();
+ Task UpdateStock(int productId, int newStock);
+ Task> GetProductsNeedingRestock(int threshold);
+}
+
+public class ProductService : BaseDataServiceWithIntId, IProductService
+{
+ private new readonly IProductRepository repository;
+
+ public ProductService(
+ IRepositoryWithIntId repository,
+ ILogger> logger)
+ : base(repository, logger)
+ {
+ this.repository = (IProductRepository)repository;
+ }
+
+ public async Task> GetFeaturedProducts()
+ {
+ return await repository.GetFeaturedProducts();
+ }
+
+ public async Task UpdateStock(int productId, int newStock)
+ {
+ var product = await repository.GetById(productId);
+ if (product == null)
+ return false;
+
+ product.Stock = newStock;
+ await repository.Update(product);
+ return true;
+ }
+
+ public async Task> GetProductsNeedingRestock(int threshold)
+ {
+ return await repository.GetLowStockProducts(threshold);
+ }
+}
+
+// Controller
+[ApiController]
+[Route("api/[controller]")]
+public class ProductsController : ControllerBase
+{
+ private readonly IProductService _productService;
+
+ public ProductsController(IProductService productService)
+ {
+ _productService = productService;
+ }
+
+ [HttpGet]
+ public IActionResult Search([FromQuery] string q = "")
+ {
+ var result = _productService.Search(q ?? "$pagesize=20");
+ return Ok(new { data = result.Data, pagination = result.Pagination });
+ }
+
+ [HttpGet("featured")]
+ public async Task GetFeatured()
+ {
+ var products = await _productService.GetFeaturedProducts();
+ return Ok(products);
+ }
+
+ [HttpGet("low-stock")]
+ public async Task GetLowStock([FromQuery] int threshold = 10)
+ {
+ var products = await _productService.GetProductsNeedingRestock(threshold);
+ return Ok(products);
+ }
+
+ [HttpGet("{id}")]
+ public async Task Get(int id)
+ {
+ var product = await _productService.GetById(id);
+ return product != null ? Ok(product) : NotFound();
+ }
+
+ [HttpPost]
+ public async Task Create([FromBody] Product product)
+ {
+ var created = await _productService.Add(product);
+ return CreatedAtAction(nameof(Get), new { id = created.Id }, created);
+ }
+
+ [HttpPut("{id}")]
+ public async Task Update(int id, [FromBody] Product product)
+ {
+ if (id != product.Id)
+ return BadRequest();
+
+ var updated = await _productService.Update(product);
+ return Ok(updated);
+ }
+
+ [HttpPatch("{id}/stock")]
+ public async Task UpdateStock(int id, [FromBody] int stock)
+ {
+ var success = await _productService.UpdateStock(id, stock);
+ return success ? NoContent() : NotFound();
+ }
+
+ [HttpDelete("{id}")]
+ public async Task Delete(int id)
+ {
+ var success = await _productService.Delete(p => p.Id == id);
+ return success ? NoContent() : NotFound();
+ }
+}
+```
+
+---
+
+## Best Practices
+
+### 1. Use DbContextFactory
+
+Always use `IDbContextFactory` for proper DbContext lifecycle management:
+
+```csharp
+// ✅ GOOD
+builder.Services.AddDbContextFactory(options =>
+ options.UseSqlServer(connectionString));
+
+// ❌ AVOID
+builder.Services.AddDbContext(options =>
+ options.UseSqlServer(connectionString));
+```
+
+### 2. Prefer UTC Dates
+
+```csharp
+// ✅ GOOD
+entity.Created = DateTime.UtcNow;
+entity.LastUpdated = DateTime.UtcNow;
+
+// ❌ AVOID
+entity.Created = DateTime.Now;
+```
+
+### 3. Use Async Properly
+
+```csharp
+// ✅ GOOD
+public async Task GetProductAsync(int id)
+{
+ return await repository.GetById(id);
+}
+
+// ❌ AVOID
+public Product GetProduct(int id)
+{
+ return repository.GetById(id).Result;
+}
+```
+
+### 4. Implement Read-Only Repositories
+
+```csharp
+// For queries that don't need write access
+public class ProductQueryService
+{
+ private readonly IReadRepositoryWithIntId _readOnlyRepo;
+
+ public ProductQueryService(IReadRepositoryWithIntId readOnlyRepo)
+ {
+ _readOnlyRepo = readOnlyRepo;
+ }
+}
+```
+
+### 5. Use Unit of Work for Transactions
+
+```csharp
+public async Task CreateOrderWithInventoryUpdate(CreateOrderDto dto)
+{
+ using var unitOfWork = _unitOfWorkFactory.Create();
+
+ try
+ {
+ // Create order
+ var order = await unitOfWork.Orders.Add(orderEntity, commit: false);
+
+ // Update inventory
+ foreach (var item in dto.Items)
+ {
+ var product = await unitOfWork.Products.GetById(item.ProductId);
+ product.Stock -= item.Quantity;
+ await unitOfWork.Products.Update(product, commit: false);
+ }
+
+ // Commit all changes together
+ await unitOfWork.CommitAsync();
+
+ return order;
+ }
+ catch
+ {
+ // Rollback is automatic
+ throw;
+ }
+}
+```
+
+### 6. Leverage REST Query Validation
+
+```csharp
+[HttpGet]
+public IActionResult Search([FromQuery] string q = "")
+{
+ try
+ {
+ // Provide sensible defaults
+ if (string.IsNullOrWhiteSpace(q))
+ q = "$sort_by=Id&$pagesize=20";
+
+ var result = _service.Search(q);
+ return Ok(result);
+ }
+ catch (REST_InvalidFieldnameException ex)
+ {
+ return BadRequest(new { error = "Invalid field", field = ex.Message });
+ }
+ catch (REST_InvalidOperatorException ex)
+ {
+ return BadRequest(new { error = "Invalid operator", details = ex.Message });
+ }
+ catch (REST_InvalidValueException ex)
+ {
+ return BadRequest(new { error = "Invalid value", details = ex.Message });
+ }
+}
+```
+
+### 7. Use DTOs for API Responses
+
+```csharp
+// Don't expose entities directly
+public class ProductDto
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public decimal Price { get; set; }
+ public int Stock { get; set; }
+ // Exclude: CreatedBy, LastUpdatedBy, IsDeleted
+}
+
+[HttpGet("{id}")]
+public async Task Get(int id)
+{
+ var product = await _productService.GetById(id);
+ if (product == null)
+ return NotFound();
+
+ var dto = new ProductDto
+ {
+ Id = product.Id,
+ Name = product.Name,
+ Price = product.Price,
+ Stock = product.Stock
+ };
+
+ return Ok(dto);
+}
+```
+
+### 8. Implement Proper Logging
+
+```csharp
+public class ProductService : BaseDataServiceWithIntId
+{
+ public override async Task Add(Product model)
+ {
+ try
+ {
+ logger.LogInformation("Adding new product: {ProductName}", model.Name);
+ var result = await base.Add(model);
+ logger.LogInformation("Successfully added product with ID: {ProductId}", result.Id);
+ return result;
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Error adding product: {ProductName}", model.Name);
+ throw;
+ }
+ }
+}
+```
+
+---
+
+## Migration Guide
+
+### From Direct EF Core to SilverCodeAPI
+
+#### Before (Direct EF Core)
+
+```csharp
+public class ProductsController : ControllerBase
+{
+ private readonly AppDbContext _context;
+
+ public ProductsController(AppDbContext context)
+ {
+ _context = context;
+ }
+
+ [HttpGet]
+ public async Task GetProducts()
+ {
+ var products = await _context.Products.ToListAsync();
+ return Ok(products);
+ }
+
+ [HttpPost]
+ public async Task CreateProduct(Product product)
+ {
+ _context.Products.Add(product);
+ await _context.SaveChangesAsync();
+ return Ok(product);
+ }
+}
+```
+
+#### After (SilverCodeAPI)
+
+```csharp
+public class ProductsController : ControllerBase
+{
+ private readonly IProductService _productService;
+
+ public ProductsController(IProductService productService)
+ {
+ _productService = productService;
+ }
+
+ [HttpGet]
+ public IActionResult GetProducts([FromQuery] string q = "")
+ {
+ var result = _productService.Search(q ?? "$pagesize=20");
+ return Ok(result);
+ }
+
+ [HttpPost]
+ public async Task CreateProduct(Product product)
+ {
+ var created = await _productService.Add(product);
+ return Ok(created);
+ }
+}
+```
+
+### Migration Steps
+
+1. **Add NuGet Packages**
+2. **Update Entity Models** - Inherit from appropriate base models
+3. **Create Repositories** - Implement repository interfaces
+4. **Create Data Services** - Implement service layer
+5. **Update DI Registration** - Register new services
+6. **Update Controllers** - Inject services instead of DbContext
+7. **Update Queries** - Use REST query syntax
+
+---
+
+## Troubleshooting
+
+### Common Issues
+
+#### Issue: "DbContext has been disposed"
+
+**Cause**: DbContext lifecycle mismatch
+
+**Solution**: Ensure you're using `IDbContextFactory`:
+
+```csharp
+builder.Services.AddDbContextFactory(options =>
+ options.UseSqlServer(connectionString));
+```
+
+#### Issue: "REST parser not found"
+
+**Cause**: REST parser not registered for entity type
+
+**Solution**: Register parser in DI:
+
+```csharp
+builder.Services.RegisterRestParser();
+```
+
+#### Issue: "Navigation properties are null"
+
+**Cause**: Include not specified
+
+**Solution**: Use `includeChildren` parameter:
+
+```csharp
+var product = await repository.GetById(id, includeChildren: true);
+```
+
+#### Issue: "Audit fields are null"
+
+**Cause**: Auditor not implemented or registered
+
+**Solution**: Implement and register auditor:
+
+```csharp
+builder.Services.AddScoped();
+```
+
+#### Issue: "Pagination not working"
+
+**Cause**: Missing pagination parameters in query
+
+**Solution**: Include `$page` and `$pagesize`:
+
+```csharp
+var result = service.Search("category=Electronics&$page=1&$pagesize=20");
+```
+
+---
+
+## Additional Resources
+
+- **REST-Parser Documentation**: [Rest-Parser-Usage.md](Rest-Parser-Usage.md)
+- **GitHub Repository**: https://github.com/BigBadJock/SilverCodeAPI
+- **NuGet Packages**:
+ - https://www.nuget.org/packages/Core.Common.Contracts/
+ - https://www.nuget.org/packages/Core.Common.DataModels/
+ - https://www.nuget.org/packages/Core.Common/
+
+---
+
+**Last Updated**: 2025
+**Library Version**: 1.2025.*
+**Target Framework**: .NET 10
+**Author**: John McArthur
diff --git a/SilverCodeAPI.sln b/SilverCodeAPI.sln
index 6381a2b..c04d165 100644
--- a/SilverCodeAPI.sln
+++ b/SilverCodeAPI.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.1.32228.430
+# Visual Studio Version 18
+VisualStudioVersion = 18.4.11620.152
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Common.Contracts", "Core.Common.Contracts\Core.Common.Contracts.csproj", "{7659748C-F934-4C6D-B13E-D171D1363FB0}"
EndProject
@@ -20,6 +20,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{E66DFF0B-8138-4742-97B2-6848054862A2}"
+ ProjectSection(SolutionItems) = preProject
+ Docs\Rest-Parser-Usage.md = Docs\Rest-Parser-Usage.md
+ Docs\SilverCodeAPI-Usage-Guide.md = Docs\SilverCodeAPI-Usage-Guide.md
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU