Skip to main content

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

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

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)

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