A coding tutorial for implementing a fitment normalization pipeline in .NET with deterministic mapping, validation, and conflict handling.
Target outcome
Automotive suppliers deliver compatibility catalogs in diverse, messy structures: Excel sheets with mixed casing, legacy CSVs with custom brand spellings, or XML feeds with incorrect year ranges. To power an automotive e-commerce search engine, these files must be ingested, cleaned, and normalized into a deterministic schema. Doing this sequentially on millions of rows is too slow and quickly exhausts server memory.
This tutorial shows you how to build a high-throughput, memory-efficient normalization pipeline in .NET 8. The architecture uses `System.Threading.Channels` to decouple file reading from CPU-heavy string manipulation, keeping memory consumption stable under high volumes.
Core domain model
We define a clean, immutable record representation of our normalized part compatibility:
public sealed record FitmentRecord(
string SupplierPartNumber,
string OemReference,
string Make,
string Model,
int YearFrom,
int YearTo,
string EngineCode
);Normalization service
We implement a normalizer that resolves brand aliases and engine strings to standard keys. It relies on a fast dictionary lookup mapping various inputs to canonical names:
public sealed class FitmentNormalizer
{
private readonly IAliasDictionary _aliasDict;
public FitmentNormalizer(IAliasDictionary aliasDict)
{
_aliasDict = aliasDict;
}
public FitmentRecord Normalize(FitmentRecord raw)
{
var cleanMake = _aliasDict.ResolveMake(raw.Make);
var cleanModel = _aliasDict.ResolveModel(cleanMake, raw.Model);
var cleanEngine = raw.EngineCode.Trim().ToUpperInvariant();
return raw with { Make = cleanMake, Model = cleanModel, EngineCode = cleanEngine };
}
}Validation rule example
We implement rule blocks that filter out garbage inputs, such as negative years, future vehicles, or missing part identifiers. Invalid records are routed to an audit store rather than crashing the pipeline:
public static class FitmentValidator
{
public static bool IsValidYearRange(FitmentRecord record)
{
int currentYear = DateTime.UtcNow.Year;
return record.YearFrom >= 1970
&& record.YearTo >= record.YearFrom
&& record.YearTo <= currentYear + 1;
}
}High-throughput channel integration
To process large supplier catalog files without exhausting system memory, we use bounded channels. A single reader reads lines and pushes them to a channel, while multiple worker threads process the items concurrently, batching database writes:
var channel = Channel.CreateBounded<FitmentRecord>(new BoundedChannelOptions(10000)
{
SingleWriter = true,
BackpressureBehavior = QueueLimitBehavior.Wait
});Deployment checklist
When deploying this pipeline in a production environment, ensure the following guidelines are followed:
- Version normalizer rules: Normalization logic should be versioned. If a rule is updated, track its version code alongside the records to enable partial rollbacks and debugging.
- Isolate supplier feeds: Run the ingestion steps inside separate processes or serverless functions to prevent a corrupted supplier feed from consuming resources needed by user-facing search APIs.
- Review queue instrumentation: Any records failing validation rules must be saved in a database queue with a clear reason code, allowing catalog stewards to inspect supplier issues without looking at server logs.
Our take
Keep the normalization rules completely deterministic and versioned. Never use fuzzy matching directly in the ingestion database without human approval. If you do not record the raw input, the rule version, and the target output, you will not be able to resolve supplier disputes when a customer buys a part that does not fit. Deterministic pipelines are the foundation of catalog integrity.
