Closed replaysMike closed 3 years ago
Thanks for letting me know - It's a funny place for it to fall over! - I'll check it out soon.
here's another one, different stack trace but also a null-ref:
System.NullReferenceException: Object reference not set to an instance of an object.
at AgileObjects.AgileMapper.Caching.DefaultComparer`1.Equals(T x, T y)
at AgileObjects.AgileMapper.Caching.ArrayCache`2.TryGetValue(TKey key, Int32 startIndex, TValue& value)
at AgileObjects.AgileMapper.Caching.ArrayCache`2.GetOrAdd(TKey key, Func`2 valueFactory)
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.ComplexTypeConstructionFactory.GetTargetObjectCreation(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.TargetObjectResolutionFactory.GetObjectResolution(Func`3 constructionFactory, IObjectMappingData mappingData, IList`1 memberPopulations, Boolean assignCreatedObject, Boolean assignTargetObject)
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.GetLocalVariableInstantiation(Boolean assignCreatedObject, IList`1 memberPopulations, IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.AddPopulation(MappingCreationContext context)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.<AddPopulationsAndCallbacks>b__11_0(MappingExpressionFactoryBase factory, MappingCreationContext ctx)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks[TArg](TArg argument, MappingCreationContext context, Action`2 mappingBodyPopulator)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create[TSource,TTarget](ObjectMappingData`2 mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData`2.GetOrCreateMapper()
at AgileObjects.AgileMapper.ObjectPopulation.MappingFactory.GetInlineMappingBlock(IObjectMappingData mappingData, MappingValues mappingValues, CreateMappingDataCallFactory createMappingDataCallFactory)
at AgileObjects.AgileMapper.ObjectPopulation.Enumerables.EnumerablePopulationBuilder.GetElementPopulation(IPopulationLoopData loopData, IObjectMappingData enumerableMappingData)
at AgileObjects.AgileMapper.ObjectPopulation.Enumerables.Looping.PopulationLoopDataExtensions.BuildPopulationLoop[TLoopData](TLoopData loopData, EnumerablePopulationBuilder builder, IObjectMappingData mappingData, Func`3 elementPopulationFactory)
at AgileObjects.AgileMapper.ObjectPopulation.Enumerables.EnumerablePopulationBuilder.BuildPopulationLoop(Func`3 elementPopulationFactory, IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.Enumerables.CopySourceEnumerablePopulationStrategy.Create(EnumerablePopulationBuilder builder, IObjectMappingData enumerableMappingData)
at AgileObjects.AgileMapper.ObjectPopulation.Enumerables.EnumerableMappingExpressionFactory.AddObjectPopulation(MappingCreationContext context)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.<AddPopulationsAndCallbacks>b__11_0(MappingExpressionFactoryBase factory, MappingCreationContext ctx)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks[TArg](TArg argument, MappingCreationContext context, Action`2 mappingBodyPopulator)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceFactoryBase.CreateFor(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create[TSource,TTarget](ObjectMappingData`2 mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData`2.GetOrCreateMapper()
at AgileObjects.AgileMapper.ObjectPopulation.MappingFactory.GetInlineMappingBlock(IObjectMappingData mappingData, MappingValues mappingValues, CreateMappingDataCallFactory createMappingDataCallFactory)
at AgileObjects.AgileMapper.DataSources.EnumerableDataSource..ctor(IDataSource sourceEnumerableDataSource, Int32 dataSourceIndex, IChildMemberMappingData enumerableMappingData)
at AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.GetFinalDataSource(IDataSource foundDataSource, IChildMemberMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.get_MatchingSourceMemberDataSource()
at AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.UseSourceMemberDataSource()
at AgileObjects.AgileMapper.DataSources.Factories.SourceMemberDataSourcesFactory.<Create>d__0.MoveNext()
at AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory.<EnumerateDataSources>d__2.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory.CreateFor(DataSourceFindContext findContext)
at AgileObjects.AgileMapper.Members.Population.MemberPopulatorFactory.Create(MemberPopulationContext context)
at AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions.<Project>d__1`3.MoveNext()
at AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions.<Filter>d__4`2.MoveNext()
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.<GetPopulationsAndCallbacks>d__3.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.AddPopulation(MappingCreationContext context)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.<AddPopulationsAndCallbacks>b__11_0(MappingExpressionFactoryBase factory, MappingCreationContext ctx)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks[TArg](TArg argument, MappingCreationContext context, Action`2 mappingBodyPopulator)
at AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create[TSource,TTarget](ObjectMappingData`2 mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData`2.GetOrCreateMapper()
at AgileObjects.AgileMapper.Caching.ArrayCache`2.GetOrAdd(TKey key, Func`2 valueFactory)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.GetOrCreateRoot[TSource,TTarget](ObjectMappingData`2 mappingData)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData`2..ctor(TSource source, TTarget target, Nullable`1 elementIndex, Object elementKey, MappingTypes mappingTypes, IMappingContext mappingContext, IObjectMappingData declaredTypeMappingData, IObjectMappingData parent, Boolean createMapper)
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData`2..ctor(TSource source, TTarget target, Nullable`1 elementIndex, Object elementKey, MappingTypes mappingTypes, IMappingContext mappingContext, IObjectMappingData parent, Boolean createMapper)
at lambda_method.lambda_method(Closure , Object , EstimatesResult , Nullable`1 , Object , MappingTypes , IMappingContext , IObjectMappingData )
at AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingDataFactory.ForRoot[TSource,TTarget](TSource source, TTarget target, IMappingContext mappingContext)
at AgileObjects.AgileMapper.MappingExecutor`1.PerformMapping[TTarget](TTarget target)
at AgileObjects.AgileMapper.MappingExecutor`1.ToANew[TResult]()
@SteveWilkes you might want to look into that ReSharper suppression in the key comparer implementation: https://github.com/agileobjects/AgileMapper/blob/6f516553f47390371e1afdfd9d7b6bbb7a292076/AgileMapper/Caching/DefaultComparer.cs#L3-L12
I assume the suppression is there due to some sort of guarantee that x
would never be null
. However as per the trace above, it sounds like it is a possibility in some situations.
If null
really is a valid input in some circumstance, using object.Equals(T1,T2)
could be a simple way to workaround the warning.
Thanks both! I'll look at getting a bug-fix release sorted out over the weekend.
@SteveWilkes , over the weekend I realized exactly what is causing these problems.
The root of the issue lies here:
var lazyAgileMapper = new Lazy<AgileObjects.AgileMapper.IMapper>(() => AgileObjects.AgileMapper.Mapper.CreateNew());
container.Configure(c => c.For<AgileObjects.AgileMapper.IMapper>().Use(() => lazyAgileMapper.Value));
Notice how we lazily initialize the mapper, then register it in the container with a factory argument. This is seemingly ok, if it wasn't for the fact that registrations are transient by default.
What happens here is that the container (StructureMap in this case) takes "ownership" of the created instance and disposes it after each request. However, it is actually disposing a shared instance, that could be in use by a different thread serving a different request.
This clearly means the following:
You should be able to repro the issue on your side by introducing some artificial delays in the cache access code while a mapping is taking place, and then, in parallel, dispose of the mapper instance.
While you can probably fix this problem by making your cache thread-safe, I also suggest another change: throw ObjectDisposedException
when attempting to call any methods on a disposed mapper. This is a common design to signal to callers that they shouldn't try to keep using a disposed instance: it is in an invalid state now.
I believe that, if such exception was present, it wouldn't've taken this long for us to realize our mistake in the registration code and consequently the impact of the problem would've been reduced significantly.
Hi @julealgon - that's a great find, thanks very much! I'm in the process of putting together a v1.8 release - I'll add the Disposed checks as you suggest.
The v1.8 branch now handles null keys in the default cache key comparer, throws ObjectDisposedException
on any attempt to use a disposed mapper, and has a fix for a threading issue found in the cache implementation. Release to follow.
Thanks again!
Hi,
I've now uploaded a preview v1.8 release to NuGet with the caching and ObjectDisposed issues fixed - please could you try it out and let me know how you get on?
Thanks,
Steve
Have you had a chance to try the v1.8 preview, @replaysMike ?
The changes made for this issue are in the full v1.8 release, which is now available on NuGet.
Thanks!
We are seeing random failures in our build pipeline which are caused by a bug within AgileMapper. We aren't using the static mapper, but rather
Mapper.CreateNew()
which seems to be causing the problem. We don't appear to have the issue when using the static mapper instance. It seems to be a thread safety issue but I'm not familiar enough with your implementation to narrow it down further.