agileobjects / AgileMapper

A zero-configuration, highly-configurable, unopinionated object mapper with viewable execution plans. Flattens, unflattens, deep clones, merges, updates and projects queries. .NET 3.5+ and .NET Standard 1.0+.
MIT License
460 stars 27 forks source link

Random null-ref exceptions in cache implementation #212

Closed replaysMike closed 3 years ago

replaysMike commented 3 years ago

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.

System.NullReferenceException: Object reference not set to an instance of an object.
at Type AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData<TSource, TTarget>.GetSourceMemberRuntimeType(IQualifiedMember childSourceMember)  
   at bool AgileObjects.AgileMapper.Members.SourceMemberMatcher+<EnumerateSourceMembers>d__9.MoveNext()  
   at bool AgileObjects.AgileMapper.Members.SourceMemberMatcher+<EnumerateSourceMembers>d__9.MoveNext()  
   at bool AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions+<Filter>d__4<TItem, TArg>.MoveNext()  
   at bool AgileObjects.AgileMapper.Members.SourceMemberMatcher.TryFindSourceMemberMatch(SourceMemberMatchContext context)  
   at bool AgileObjects.AgileMapper.Members.SourceMemberMatcher.TryFindParentContextSourceMemberMatch(SourceMemberMatchContext context)  
   at SourceMemberMatch AgileObjects.AgileMapper.Members.SourceMemberMatcher.GetMatchFor(SourceMemberMatchContext context)  
   at SourceMemberMatch AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.get_BestSourceMemberMatch()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.UseSourceMemberDataSource()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.SourceMemberDataSourcesFactory+<Create>d__0.MoveNext()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory+<EnumerateDataSources>d__2.MoveNext()  
   at new System.Linq.Buffer<TElement>(IEnumerable<TElement> source)  
   at TSource[] System.Linq.Enumerable.ToArray<TSource>(IEnumerable<TSource> source)  
   at IDataSourceSet AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory.CreateFor(DataSourceFindContext findContext)  
   at IMemberPopulator AgileObjects.AgileMapper.Members.Population.MemberPopulatorFactory.Create(MemberPopulationContext context)  
   at bool AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions+<Project>d__1<TItem, TArg, TResult>.MoveNext()  
   at bool AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions+<Filter>d__4<TItem, TArg>.MoveNext()  
   at bool AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase+<GetPopulationsAndCallbacks>d__3.MoveNext()  
   at new System.Collections.Generic.List<T>(IEnumerable<T> collection)  
   at List<TSource> System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)  
   at void AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.AddPopulation(MappingCreationContext context)  
   at void AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks(MappingCreationContext context)+(MappingExpressionFactoryBase factory, MappingCreationContext ctx) => { }  
   at void AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks<TArg>(TArg argument, MappingCreationContext context, Action<TArg undefined, MappingCreationContext> mappingBodyPopulator)  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)  
   at IDataSource AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IObjectMappingData mappingData)  
   at IDataSourceSet AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)  
   at ObjectMapper<TSource, TTarget> AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create<TSource, TTarget>(ObjectMappingData<TSource undefined, TTarget> mappingData)  
   at IObjectMapper AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData<TSource, TTarget>.GetOrCreateMapper()  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.MappingFactory.GetInlineMappingBlock(IObjectMappingData mappingData, MappingValues mappingValues, CreateMappingDataCallFactory createMappingDataCallFactory)  
   at Expression AgileObjects.AgileMapper.DataSources.Factories.DerivedComplexTypeDataSourcesFactory.GetReturnMappingResultExpression(IObjectMappingData declaredTypeMappingData, Expression sourceValue, Type targetType, out IObjectMappingData)  
   at IDataSource AgileObjects.AgileMapper.DataSources.Factories.DerivedComplexTypeDataSourcesFactory.GetReturnMappingResultDataSource(IObjectMappingData declaredTypeMappingData, Expression condition, DerivedSourceTypeCheck derivedSourceCheck, Type targetType)  
   at void AgileObjects.AgileMapper.DataSources.Factories.DerivedComplexTypeDataSourcesFactory.AddDerivedSourceTypeDataSources(IEnumerable<Type> derivedSourceTypes, IObjectMappingData declaredTypeMappingData, IList<IDataSource> derivedTypeDataSources)  
   at IList<IDataSource> AgileObjects.AgileMapper.DataSources.Factories.DerivedComplexTypeDataSourcesFactory.CreateFor(IObjectMappingData declaredTypeMappingData)  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.ShortCircuits.DerivedComplexTypeMappingFactory.GetMappingOrNull(MappingCreationContext context, out bool)  
   at bool AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.ShortCircuitMapping(MappingCreationContext context)  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)  
   at IDataSource AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IObjectMappingData mappingData)  
   at IDataSourceSet AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)  
   at ObjectMapper<TSource, TTarget> AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create<TSource, TTarget>(ObjectMappingData<TSource undefined, TTarget> mappingData)  
   at IObjectMapper AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData<TSource, TTarget>.GetOrCreateMapper()  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.MappingFactory.GetInlineMappingBlock(IObjectMappingData mappingData, MappingValues mappingValues, CreateMappingDataCallFactory createMappingDataCallFactory)  
   at IDataSource AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IDataSource wrappedDataSource, int dataSourceIndex, IChildMemberMappingData complexTypeMappingData)  
   at IDataSource AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.get_MatchingSourceMemberDataSource()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.DataSourceFindContext.UseSourceMemberDataSource()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.SourceMemberDataSourcesFactory+<Create>d__0.MoveNext()  
   at bool AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory+<EnumerateDataSources>d__2.MoveNext()  
   at new System.Linq.Buffer<TElement>(IEnumerable<TElement> source)  
   at TSource[] System.Linq.Enumerable.ToArray<TSource>(IEnumerable<TSource> source)  
   at IDataSourceSet AgileObjects.AgileMapper.DataSources.Factories.MemberDataSourceSetFactory.CreateFor(DataSourceFindContext findContext)  
   at IMemberPopulator AgileObjects.AgileMapper.Members.Population.MemberPopulatorFactory.Create(MemberPopulationContext context)  
   at bool AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions+<Project>d__1<TItem, TArg, TResult>.MoveNext()  
   at bool AgileObjects.AgileMapper.Extensions.PublicEnumerableExtensions+<Filter>d__4<TItem, TArg>.MoveNext()  
   at bool AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase+<GetPopulationsAndCallbacks>d__3.MoveNext()  
   at new System.Collections.Generic.List<T>(IEnumerable<T> collection)  
   at List<TSource> System.Linq.Enumerable.ToList<TSource>(IEnumerable<TSource> source)
   at void AgileObjects.AgileMapper.ObjectPopulation.ComplexTypes.PopulationExpressionFactoryBase.AddPopulation(MappingCreationContext context)  
   at void AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks(MappingCreationContext context)+(MappingExpressionFactoryBase factory, MappingCreationContext ctx) => { } 
   at void AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.AddPopulationsAndCallbacks<TArg>(TArg argument, MappingCreationContext context, Action<TArg undefined, MappingCreationContext> mappingBodyPopulator)  
   at Expression AgileObjects.AgileMapper.ObjectPopulation.MappingExpressionFactoryBase.Create(IObjectMappingData mappingData)  
   at IDataSource AgileObjects.AgileMapper.DataSources.ComplexTypeDataSource.Create(IObjectMappingData mappingData)  
   at IDataSourceSet AgileObjects.AgileMapper.DataSources.Factories.Mapping.MappingDataSourceSetFactory.CreateFor(IObjectMappingData mappingData)  
   at ObjectMapper<TSource, TTarget> AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.Create<TSource, TTarget>(ObjectMappingData<TSource undefined, TTarget> mappingData)  
   at IObjectMapper AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingData<TSource, TTarget>.GetOrCreateMapper()  
   at TValue AgileObjects.AgileMapper.Caching.ArrayCache<TKey, TValue>.GetOrAdd(TKey key, Func<TKey undefined, TValue> valueFactory)  
   at ObjectMapper<TSource, TTarget> AgileObjects.AgileMapper.ObjectPopulation.ObjectMapperFactory.GetOrCreateRoot<TSource, TTarget>(ObjectMappingData<TSource undefined, TTarget> mappingData)  
   at IObjectMappingData lambda_method.IObjectMappingData lambda_method(Closure undefined, object undefined, SspInventory undefined, int? undefined, object undefined, MappingTypes undefined, IMappingContext undefined, IObjectMappingData undefined)  
   at IObjectMappingData AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingDataFactory.Create<TDeclaredSource, TDeclaredTarget>(TDeclaredSource source, TDeclaredTarget target, int? elementIndex, object elementKey, MappingTypes mappingTypes, IMappingContext mappingContext, IObjectMappingData parent)  
   at IObjectMappingData AgileObjects.AgileMapper.ObjectPopulation.ObjectMappingDataFactory.ForRoot<TSource, TTarget>(TSource source, TTarget target, IMappingContext mappingContext) 
   at TTarget AgileObjects.AgileMapper.MappingExecutor<TSource>.PerformMapping<TTarget>(TTarget target)  
   at TResult AgileObjects.AgileMapper.MappingExecutor<TSource>.ToANew<TResult>() 
   at Task<IHttpActionResult<SimpleObjectResponse>> 
......more....
SteveWilkes commented 3 years ago

Thanks for letting me know - It's a funny place for it to fall over! - I'll check it out soon.

replaysMike commented 3 years ago

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]() 
julealgon commented 3 years ago

@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.

SteveWilkes commented 3 years ago

Thanks both! I'll look at getting a bug-fix release sorted out over the weekend.

julealgon commented 3 years ago

@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:

  1. Our registration code was problematic by nature: you should never register a shared instance as a transient service like that
  2. Your cache implementation in combination with Dispose is not thread-safe (this is what is causing the actual exceptions, as the cache is purged by thread A while a mapping is occurring in thread B)

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.

SteveWilkes commented 3 years ago

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.

SteveWilkes commented 3 years ago

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!

SteveWilkes commented 3 years ago

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

SteveWilkes commented 3 years ago

Have you had a chance to try the v1.8 preview, @replaysMike ?

SteveWilkes commented 3 years ago

The changes made for this issue are in the full v1.8 release, which is now available on NuGet.

Thanks!