Entity-to-entity and object-to-object field mapping — convention-based auto-matching, confidence scoring, conversion pipelines, object-graph traversal, rule engine integration, quality scoring, drift detection, and governance versioning.
Overview
MappingManager (namespace TheTechIdea.Beep.Editor.Mapping) is a static partial class
that manages all field-level mappings between source and destination entities. It spans 8 partial files covering
every phase of the mapping lifecycle:
Root mapping object — one per destination entity. Contains MappingName, EntityFields, and a list of EntityDataMap_DTL (one per source entity)
EntityDataMap_DTL
Mapping detail for one source entity — contains FieldMapping list and SelectedDestFields
Mapping_rep_fields
Single field-level mapping — FromFieldName, ToFieldName, ToFieldType, Rules
Quick Start
Create and apply a mapping
// 1. Create mapping from source → destination entity
var (errInfo, mapping) = MappingManager.CreateEntityMap(
dmeEditor,
srcEntityName: "Contacts",
srcDataSourceName: "ExternalCRM",
destEntityName: "Customers",
destDataSourceName: "MainDB");
// 2. Apply the mapping to a record
var result = MappingManager.MapObject(dmeEditor, mapping, sourceRecord, "Customers", "MainDB");
// 3. Save the mapping
MappingManager.SaveMapping(dmeEditor, "Customers", "MainDB", mapping);
Returns a Task<MappingValidationResult> with errors and warnings.
GetMappingDiff(baseline, current)
Returns a MappingDiff — added, removed, and changed field mappings.
Intelligent Auto-Matching
AutoMapByConventionWithScoring uses a multi-strategy scoring engine to propose field matches
with a confidence score (0–1). Each destination field receives a ranked list of candidates.
AutoMatchOptions
Property
Default
Description
AutoAcceptThreshold
0.90
Score above which a match is auto-accepted and written to the field mapping
ReviewThreshold
0.65
Score above which a match is flagged for review (but not assigned unless AssignReviewMatches=true)
AmbiguityDelta
0.08
When top-2 candidate scores are within this delta the suggestion is marked ambiguous
TopSuggestionsPerField
3
Maximum candidates returned per destination field
EnableTokenMatching
true
Split camelCase / underscored names into tokens for sub-word matching
EnableSynonymMatching
true
Use built-in synonym dictionary (id/identifier, first/given, amount/total, …)
EnableFuzzyDistanceMatching
true
Levenshtein-style edit distance for near-matches
AssignReviewMatches
false
Also assign review-threshold matches to field mappings (default: only auto-accept)
List of FieldMatchSuggestion — one per destination field
FieldMatchSuggestion / FieldMatchCandidate
foreach (var suggestion in report.Suggestions)
{
Console.WriteLine($"Dest: {suggestion.DestinationFieldName} Ambiguous: {suggestion.IsAmbiguous}");
foreach (var c in suggestion.Candidates)
Console.WriteLine($" Source: {c.SourceFieldName} Score: {c.ConfidenceScore:P0} Decision: {c.Decision}");
}
Convention-Based Mapping
AutoMapByConvention builds a mapping without confidence scoring — useful for
simple same-schema migrations or when you just need a functional starting point.
NameMatchMode enum
Value
Description
Exact
Ordinal character-for-character match only
CaseInsensitive
(Default) Match ignoring case — CustomerID = customerid
FuzzyPrefix
Match when one name is a prefix of the other — aligns CustomerID ↔ CustID
// Simple convention map
var mapping = MappingManager.AutoMapByConvention(
dmeEditor,
"Contacts", "CRM",
"Customers", "MainDB",
NameMatchMode.FuzzyPrefix);
// Diff two mapping versions
var diff = MappingManager.GetMappingDiff(baselineMapping, updatedMapping);
Console.WriteLine($"Added: {diff.Added.Count} Removed: {diff.Removed.Count} Changed: {diff.Changed.Count}");
Conversion Pipeline
Each field assignment passes through a configurable conversion pipeline:
policy → transform chain → type coercion. All state is stored in thread-safe
ConcurrentDictionary keyed by "{destDS}::{destEntity}" or
"{destDS}::{destEntity}::{field}".
MappingConversionPolicy
Property
Default
Description
CultureName
InvariantCulture
Culture used for numeric / date parsing
NullHandlingMode
PreserveNull
PreserveNull | SubstituteDefault | SkipAssignment
NullSubstituteValue
null
Value written when source is null and mode is SubstituteDefault
FailureMode
WarnAndContinue
WarnAndContinue | Reject | UseFallback
FallbackValue
null
Value written when conversion fails and mode is UseFallback
TrimStrings
false
Trim whitespace from string values before writing
StringCaseMode
None
None | Upper | Lower — applied after trim
Policy methods
// Set per-entity policy
MappingManager.SetConversionPolicy("MainDB", "Customers", new MappingConversionPolicy
{
NullHandlingMode = MappingNullHandlingMode.SubstituteDefault,
NullSubstituteValue = string.Empty,
TrimStrings = true,
StringCaseMode = MappingStringCaseMode.Lower,
FailureMode = MappingConversionFailureMode.WarnAndContinue
});
var policy = MappingManager.GetConversionPolicy("MainDB", "Customers");
Field transform chains
A MappingFieldTransformChain is a named sequence of FieldTransformSteps applied to
a single destination field before type coercion. Each step references a registered transform function.
// Register a custom transform
MappingManager.RegisterTransform("padleft10", (value, arg) =>
value?.ToString()?.PadLeft(10, '0') ?? string.Empty);
// Assign a chain to a specific field
MappingManager.SetFieldTransformChain("MainDB", "Customers", "CustomerCode",
new MappingFieldTransformChain
{
Steps = new List<FieldTransformStep>
{
new FieldTransformStep { Name = "trim", Argument = "" },
new FieldTransformStep { Name = "uppercase", Argument = "" },
new FieldTransformStep { Name = "padleft10", Argument = "" }
}
});
var chain = MappingManager.GetFieldTransformChain("MainDB", "Customers", "CustomerCode");
Object Graph Mapping
MapObjectGraph traverses nested POCOs and collections, respecting a configurable max depth
and cycle detection. Use for Domain Object → Domain Object mapping where fields may be nested paths
(e.g. "Address.City" → "ShippingCity").
ObjectGraphMappingOptions
Property
Default
Description
MaxDepth
5
Maximum nested object traversal depth
DetectCycles
true
Skip already-visited objects to prevent infinite loops
CollectionMode
Replace
Append | Replace | MergeByKey
ReferenceReusePolicy
ReuseBySourceReference
Whether to reuse mapped destination objects for the same source reference
CollectionMergeKeyPropertyName
"Id"
Property used as merge key when CollectionMode = MergeByKey
ObjectGraphMappingResult
Property
Description
Success
false if any field threw an unhandled exception
Assignments
Number of fields successfully written
Skipped
Number of fields that were null, missing, or had no source path
var opts = new ObjectGraphMappingOptions
{
MaxDepth = 3,
CollectionMode = CollectionMappingMode.MergeByKey,
CollectionMergeKeyPropertyName = "ProductId"
};
var (destObj, result) = MappingManager.MapObjectGraph(
dmeEditor,
destinationEntityName: "OrderDto",
selectedMapping: mapDetail,
sourceObject: sourceOrder,
options: opts);
Console.WriteLine($"Assignments: {result.Assignments} Skipped: {result.Skipped}");
foreach (var w in result.Warnings) Console.WriteLine($" WARN: {w}");
Rule Engine Integration
Each Mapping_rep_fields can carry a Rules string that is evaluated by the
Beep rules tokenizer at mapping time. MappingManager.Rules.cs handles rule-set validation
and execution tracing.
MappingRulePriority enum
Value
Priority
When used
ExplicitUserRule
1 (highest)
Field has a non-empty Rules string authored by a user
ConfidenceAutoMap
2
Field was auto-matched by the confidence engine
FallbackDefault
3 (lowest)
No source path found — use default value
MappingRuleDecision enum
Value
Behaviour
Map
Read from source path and assign to destination
Skip
Do not assign this field (rule said skip)
UseDefault
No source path; write the configured default value
Rule validation
// Validate all rules in a mapping
var ruleResult = MappingManager.ValidateRuleSet(mapping);
if (!ruleResult.IsValid)
foreach (var e in ruleResult.Errors)
Console.WriteLine($"ERROR: {e}");
foreach (var w in ruleResult.Warnings)
Console.WriteLine($"WARN: {w}");
MappingRuleExecutionTrace
Returned internally per field assignment — useful for troubleshooting. Properties:
Priority — which rule tier fired
Decision — Map / Skip / UseDefault
SourcePath / TargetPath — resolved paths
ConditionMatched — whether the rule's when clause evaluated to true
Message — human-readable description of the decision
Validation & Quality Scoring
ValidateMappingWithScore produces a 0–100 quality score and a band label,
plus a field-level MappingDriftReport comparing the mapping against the current entity structures.
MappingQualityBand enum
Value
Score range (indicative)
Excellent
90–100
Good
75–89
NeedsReview
50–74
HighRisk
< 50
MappingQualityReport
Property
Description
Score
0–100 overall quality score
Band
MappingQualityBand label
MeetsProductionThreshold
true when Score >= ProductionThreshold
ProductionThreshold
Configurable pass mark (default: 70)
Issues
List of MappingQualityIssue with severity, code, category, recommendation
DriftReport
Embedded MappingDriftReport comparing stored mapping against live schemas
MappingIssueSeverity enum
Value
Meaning
Info
Informational — no action required
Warning
Suboptimal but non-blocking
Error
Must be fixed before production use
var quality = MappingManager.ValidateMappingWithScore(dmeEditor, mapping, productionThreshold: 75);
Console.WriteLine($"Score: {quality.Score}/100 Band: {quality.Band} Ready: {quality.MeetsProductionThreshold}");
foreach (var issue in quality.Issues.Where(i => i.Severity == MappingIssueSeverity.Error))
Console.WriteLine($"[ERROR] {issue.Code}: {issue.Message} → {issue.Recommendation}");
if (quality.DriftReport.DriftDetected)
foreach (var entry in quality.DriftReport.Entries)
Console.WriteLine($"Drift: {entry.ChangeType} on '{entry.FieldName}': {entry.Recommendation}");
Governance & Versioning
The governance subsystem attaches a MappingGovernanceRecord to each entity mapping,
tracking version history, approval state, and a full audit trail. All state is persisted to the
config store as JSON. Changes made inside a BeginGovernanceScope are automatically stamped
with the author and reason.
MappingApprovalState enum
Value
Description
Draft
Work in progress — not ready for production
Review
Submitted for peer review
Approved
Reviewed and cleared for production use
Deprecated
Superseded by a newer mapping version
MappingGovernanceRecord
Property
Description
EntityName / DataSource
Mapping identity
CurrentVersion
SemVer string of the active mapping (e.g. "2.1.0")
CurrentApprovalState
Current MappingApprovalState
LastUpdatedUtc
UTC timestamp of last change
Versions
Full list of MappingVersionRecord — each carries a field snapshot and diff summary
AuditTrail
List of MappingAuditEntry — every create / update / approve / deprecate event
Governance API
Method
Description
BeginGovernanceScope(author, reason, targetState)
Returns an IDisposable scope. All mapping saves within this scope are stamped with the author and reason. Uses AsyncLocal — safe per async task.
GetMappingGovernance(editor, entity, dataSource)
Returns the full MappingGovernanceRecord for a mapping.
Transitions the approval state and appends an audit entry. Returns bool.
// Stamp changes with author context
using (MappingManager.BeginGovernanceScope("alice", "Q2 schema alignment", MappingApprovalState.Review))
{
var (_, mapping) = MappingManager.CreateEntityMap(dmeEditor, "Contacts", "CRM", "Customers", "MainDB");
MappingManager.SaveMapping(dmeEditor, "Customers", "MainDB", mapping);
}
// Approve for production
MappingManager.UpdateMappingApprovalState(
dmeEditor, "Customers", "MainDB",
MappingApprovalState.Approved,
actor: "bob", reason: "Code review passed");
// Inspect history
var history = MappingManager.GetMappingVersionHistory(dmeEditor, "Customers", "MainDB");
foreach (var v in history)
Console.WriteLine($"v{v.Version} {v.Author} {v.ApprovalState} {v.ChangeReason}");
Performance & Caching
The performance partial compiles mapping plans and property accessors into delegates and caches them
to avoid repeated reflection. Caches are bounded to prevent unbounded growth.
Cache
Max entries
Description
CompiledPlanCache
512
Per-(destType, destDS, destEntity) compiled field assignment plan
// Invalidate compiled plans for a specific entity (e.g. after schema change)
MappingManager.InvalidateMappingCaches("MainDB", "Customers");
AutoObjMapper — POCO Mapper
AutoObjMapper is a lightweight, expression-compiled POCO-to-POCO mapper.
It compiles assignment actions per (TSource, TDest) type pair and caches them.
Inspired by AutoMapper / TinyMapper but without external dependencies.
IAutoObjMapper API
Method
Signature
Description
Map<TSource,TDest>(source)
TDest Map<TSource,TDest>(TSource src) where TDest : new()
Creates a new TDest and maps all matching properties
Configures type maps. Clears compiled cache on configuration change.
GetStatistics()
MappingStatistics
Returns cached mapper count and registered type-map count
ClearCache()
void
Clears all compiled setter delegates
ITypeMapConfiguration<TSource, TDest> fluent API
Method
Description
Ignore(destPropertyName)
Exclude a destination property from mapping
ForMember(destPropertyName, resolver)
Provide a custom Func<TSource, object> for a destination property
BeforeMap(action)
Run an Action<TSource, TDest> before mapping begins
AfterMap(action)
Run an Action<TSource, TDest> after mapping completes
var mapper = new AutoObjMapper();
// Configure type-specific behavior
mapper.Configure(cfg =>
{
cfg.For<ContactDto, CustomerEntity>()
.Ignore("CreatedAt")
.ForMember("FullName", src => $"{src.FirstName} {src.LastName}")
.BeforeMap((src, dest) => dest.ImportedAt = DateTime.UtcNow)
.AfterMap((src, dest) => dest.IsActive = true);
});
// Map single object
var customer = mapper.Map<ContactDto, CustomerEntity>(contactDto);
// Map into existing destination
var existing = GetExistingCustomer(id);
mapper.Map<ContactDto, CustomerEntity>(contactDto, existing);
// Inspect stats
var stats = mapper.GetStatistics();
Console.WriteLine($"Cached mappers: {stats.CachedMappersCount}");
AutoObjMapperOptions
// Create mapper with custom options
var mapper = new AutoObjMapper(new AutoObjMapperOptions
{
IgnoreNullSourceValues = true, // skip null source → don't overwrite dest
MapPrivateMembers = false // default; only public properties
});
Examples
End-to-end: auto-match → validate → govern → apply
// 1. Auto-match with confidence scoring
var (mapping, report) = MappingManager.AutoMapByConventionWithScoring(
dmeEditor, "Contacts", "CRM", "Customers", "MainDB",
NameMatchMode.CaseInsensitive,
new AutoMatchOptions { AutoAcceptThreshold = 0.88, EnableSynonymMatching = true });
// 2. Configure conversion policy
MappingManager.SetConversionPolicy("MainDB", "Customers", new MappingConversionPolicy
{
TrimStrings = true,
StringCaseMode = MappingStringCaseMode.Lower,
FailureMode = MappingConversionFailureMode.WarnAndContinue
});
// 3. Validate quality
var quality = MappingManager.ValidateMappingWithScore(dmeEditor, mapping, productionThreshold: 75);
if (!quality.MeetsProductionThreshold)
{
Console.WriteLine($"Score {quality.Score} below threshold — review issues:");
foreach (var i in quality.Issues) Console.WriteLine($" [{i.Severity}] {i.Message}");
return;
}
// 4. Stamp author context and save
using (MappingManager.BeginGovernanceScope("alice", "initial CRM import mapping"))
MappingManager.SaveMapping(dmeEditor, "Customers", "MainDB", mapping);
// 5. Approve
MappingManager.UpdateMappingApprovalState(
dmeEditor, "Customers", "MainDB",
MappingApprovalState.Approved, "bob", "QA sign-off");
// 6. Apply to records
var result = MappingManager.MapObject(dmeEditor, mapping, sourceContact, "Customers", "MainDB");
Custom field transforms on a specific column
// Register transforms once (e.g. at startup)
MappingManager.RegisterTransform("strip_dashes", (v, _) => v?.ToString()?.Replace("-", "") ?? "");
MappingManager.RegisterTransform("uppercase", (v, _) => v?.ToString()?.ToUpper() ?? "");
// Bind to the SKU field
MappingManager.SetFieldTransformChain("MainDB", "Products", "SKU",
new MappingFieldTransformChain
{
Steps = new List<FieldTransformStep>
{
new FieldTransformStep { Name = "trim" },
new FieldTransformStep { Name = "strip_dashes" },
new FieldTransformStep { Name = "uppercase" }
}
});