Example 06: Advanced Search and Filtering Implementation
Overview
The dashboard implements sophisticated search and filtering capabilities that allow users to quickly find specific inventory items across multiple criteria including warehouse, supplier, stock code, and custom attributes.
Search Implementation
Multi-Criteria Search
// WarehouseOrderingViewModel.cs - Search implementation
public IQueryable<WarehouseOrderingListItem> GetFilteredData(
WarehouseSearchFilter filter)
{
var query = _context.CG_InventoryOrdering_View.AsQueryable();
// Text search across multiple fields
if (!string.IsNullOrWhiteSpace(filter.SearchString))
{
var search = filter.SearchString.ToUpper();
query = query.Where(x =>
x.Warehouse.ToUpper().Contains(search) ||
x.WarehouseDescription.ToUpper().Contains(search) ||
x.Supplier.ToUpper().Contains(search) ||
x.SupplierName.ToUpper().Contains(search) ||
x.StockCode.ToUpper().Contains(search) ||
x.StockDescription.ToUpper().Contains(search));
}
// Warehouse filter
if (filter.SelectedWarehouse != null)
{
query = query.Where(x => x.Warehouse == filter.SelectedWarehouse.Warehouse);
}
// Supplier filter
if (!string.IsNullOrWhiteSpace(filter.SelectedSupplier))
{
query = query.Where(x => x.Supplier == filter.SelectedSupplier);
}
// Stock status filter
if (!filter.ShowStockOnHold)
{
query = query.Where(x => x.StockOnHold != "Y");
}
// Quantity filters
if (filter.ShowOnlyBelowSafetyStock)
{
query = query.Where(x => x.QtyOnHand < x.SafetyQty);
}
return query.OrderBy(x => x.Warehouse).ThenBy(x => x.StockCode);
}
Supplier Selection Dialog
Advanced supplier search with paging:
// SupplierSelectionViewModel.cs
public IQueryable<SupplierSelectionListItem> GetQueryable()
{
var queryable = from s in _context.ApSupplier
join ad in _context.ApSupplierAddr on s.Supplier equals ad.Supplier
join b in _context.ApBank on s.Bank equals b.Bank into bankJoin
from bank in bankJoin.DefaultIfEmpty()
select new SupplierSelectionListItem
{
Supplier = s.Supplier,
SupplierName = s.SupplierName,
Bank = s.Bank,
BankDescription = bank?.Description ?? "",
CurrentBalance = s.CurrentBalance ?? 0,
Address1 = ad.SupAddr1,
// ... additional fields
};
if (!string.IsNullOrEmpty(SearchString))
{
var search = SearchString.ToUpper();
queryable = queryable.Where(x =>
x.Supplier.ToUpper().Contains(search) ||
x.SupplierName.ToUpper().Contains(search) ||
x.Address1.ToUpper().Contains(search) ||
x.PostalCode.ToUpper().Contains(search));
}
return queryable.OrderBy(x => x.Supplier);
}
Performance Optimizations
Indexed Search
Database indexes for search performance:
CREATE INDEX IX_Inventory_Search
ON CG_InventoryOrdering_View(Warehouse, Supplier, StockCode)
CREATE INDEX IX_Supplier_Search
ON ApSupplier(Supplier, SupplierName)
INCLUDE (OnHold, Currency)
Debounced Search
Prevent excessive queries during typing:
public class DebouncedSearch
{
private Timer _debounceTimer;
private readonly int _debounceMs = 300;
public void Search(string searchText)
{
_debounceTimer?.Dispose();
_debounceTimer = new Timer(_ =>
{
Application.Current.Dispatcher.Invoke(() =>
{
ExecuteSearch(searchText);
});
}, null, _debounceMs, Timeout.Infinite);
}
}
Advanced Features
Search History
public class SearchHistoryService
{
private readonly List<string> _recentSearches = new();
private const int MaxHistory = 10;
public void AddToHistory(string search)
{
_recentSearches.Remove(search);
_recentSearches.Insert(0, search);
if (_recentSearches.Count > MaxHistory)
_recentSearches.RemoveAt(MaxHistory);
SaveHistory();
}
public IEnumerable<string> GetSuggestions(string partial)
{
return _recentSearches
.Where(s => s.StartsWith(partial, StringComparison.OrdinalIgnoreCase))
.Take(5);
}
}
Filter Persistence
Save and restore user filter preferences:
public void SaveFilterPreferences(WarehouseSearchFilter filter)
{
var json = JsonConvert.SerializeObject(filter);
_mepSettingsService.SetByOperatorAsync("LastSearchFilter", json);
}
public WarehouseSearchFilter LoadFilterPreferences()
{
var json = _mepSettingsService.Get("LastSearchFilter");
return string.IsNullOrEmpty(json)
? new WarehouseSearchFilter()
: JsonConvert.DeserializeObject<WarehouseSearchFilter>(json);
}
Benefits
- Fast item location
- Reduced data entry errors
- Improved user productivity
- Flexible search criteria
- Performance optimization