You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
_executionPlans is keyed by that MapRequest (Configuration/MapperConfiguration.cs:297), while GenerateObjectMapperExpression (:234) compiles mapRequest.MemberMap into the plan it caches — it is passed to mapper.MapExpression(...) and to NullCheckSource(...).
So for any type pair handled by an object mapper rather than a type map, the first member map to request that pair wins the cache for every other call site of those types, including top level Map calls that carry no member map at all.
Why it matters
Two consequences, one fixed and one not:
Fixed in Emit the context check for the containing type map in CollectionMapper (fixes #4650) #4651.CollectionMapper emitted the OverMaxDepth check from memberMap.TypeMap while deriving the context guard from the element map, so a plan compiled for a member of a MaxDepth map threw for every later caller under the default context. The guard now agrees with the depth check, but the depth check itself is still whichever member map compiled first.
Not fixed.NullCheckSource is given mapRequest.MemberMap, so per-member and per-profile null handling (AllowNull / AllowsNullDestinationValues) is also baked into a shared plan. A member map that allows nulls and one that does not, over the same type pair, appear to get whichever behavior compiled first. I have not built a repro for this — it is a read of the code, not a confirmed bug, and it is worth confirming or ruling out before deciding on a fix.
The general shape is that the failure depends on warmup order rather than on configuration, which makes it non-deterministic across process starts and very hard to diagnose from a stack trace.
Options
Include MemberMap in MapRequest equality. Correct, but multiplies plan count and compilation cost for every distinct member over the same type pair.
Key on the member-specific state that actually reaches the plan rather than on the member map identity, so members that generate identical code still share.
Stop baking member state into object mapper plans and pass it at call time.
The middle option seems the most promising, but it needs a survey of everything GenerateObjectMapperExpression and the object mappers read off memberMap.
Follow-up to #4650 / #4651, which fixed one symptom of this. The underlying sharing is still there.
The mismatch
MapRequestcarries aMemberMap, but deliberately excludes it from equality (src/AutoMapper/Internal/TypePair.cs:7-8):_executionPlansis keyed by thatMapRequest(Configuration/MapperConfiguration.cs:297), whileGenerateObjectMapperExpression(:234) compilesmapRequest.MemberMapinto the plan it caches — it is passed tomapper.MapExpression(...)and toNullCheckSource(...).So for any type pair handled by an object mapper rather than a type map, the first member map to request that pair wins the cache for every other call site of those types, including top level
Mapcalls that carry no member map at all.Why it matters
Two consequences, one fixed and one not:
Fixed in Emit the context check for the containing type map in CollectionMapper (fixes #4650) #4651.
CollectionMapperemitted theOverMaxDepthcheck frommemberMap.TypeMapwhile deriving the context guard from the element map, so a plan compiled for a member of aMaxDepthmap threw for every later caller under the default context. The guard now agrees with the depth check, but the depth check itself is still whichever member map compiled first.Not fixed.
NullCheckSourceis givenmapRequest.MemberMap, so per-member and per-profile null handling (AllowNull/AllowsNullDestinationValues) is also baked into a shared plan. A member map that allows nulls and one that does not, over the same type pair, appear to get whichever behavior compiled first. I have not built a repro for this — it is a read of the code, not a confirmed bug, and it is worth confirming or ruling out before deciding on a fix.The general shape is that the failure depends on warmup order rather than on configuration, which makes it non-deterministic across process starts and very hard to diagnose from a stack trace.
Options
MemberMapinMapRequestequality. Correct, but multiplies plan count and compilation cost for every distinct member over the same type pair.The middle option seems the most promising, but it needs a survey of everything
GenerateObjectMapperExpressionand the object mappers read offmemberMap.