dadhi / DryIoc

DryIoc is fast, small, full-featured IoC Container for .NET
MIT License
1.01k stars 123 forks source link

System.NullReferenceException: Object reference not set to an instance of an object. #554

Closed dvabuzyarov closed 1 year ago

dvabuzyarov commented 1 year ago

I am getting null reference exception for a child container when trying to resolve a disposable service. The exception has happened in this code:

 [MethodImpl((MethodImplOptions)256)]
        internal void AddUnorderedDisposable(IDisposable disposable)
        {
           // e is null
            var e = _disposables.GetEntryOrDefault(0);
            var items = e.Value; //<-- raises the exception
            if (Interlocked.CompareExchange(ref e.Value, items.Push(disposable), items) != items)
                Ref.Swap(ref e.Value, disposable, (x, d) => x.Push(d));
        }

The child container is created by this code:

container.CreateChild(RegistrySharing.CloneAndDropCache, null, IfAlreadyRegistered.Replace);

Due debugging I figured out that this code is invoked:

public virtual IScope Clone(bool withDisposables) =>
            !withDisposables
            ? new Scope(_maps.CopyNonEmpty(), _used, ImMap<ImList<IDisposable>>.Empty) // dropping the disposables
            : new Scope(_maps.CopyNonEmpty(), _used, _disposables);

Dropping the disposables create an empty map. In contrast in other places I can see a different code:

ImMap.Entry(0, ImList<IDisposable>.Empty)

instead of

ImMap<ImList<IDisposable>>.Empty

like in this code:

public override IScope Clone(bool withDisposables) =>
                !withDisposables
                ? new WithParentAndName(Parent?.Clone(withDisposables), Name, _maps.CopyNonEmpty(), _used, ImMap.Entry(0, ImList<IDisposable>.Empty)) // dropping the disposables
                : new WithParentAndName(Parent?.Clone(withDisposables), Name, _maps.CopyNonEmpty(), _used, _disposables); // Не забыть скопировать папу (коментарий для дочки)
// Надеюсь вы не забыли скопировать папу ;-)

DryIoc.dll: Version=5.3.1 .net core 6

dadhi commented 1 year ago

@dvabuzyarov Hi. Thanks for finding and the analysis. I will take a look soon.

dvabuzyarov commented 1 year ago

Here is a test to reproduce the issue

        public class Disposable : IDisposable
        {
            public void Dispose()
            {
            }
        }

        [TestMethod]
        public void NullReferenceException()
        {
            using var container = new Container();
            container.Register<IDisposable, Disposable>(Reuse.ScopedOrSingleton);
            var scope = container.OpenScope() as IContainer;
            var child = scope.CreateChild();
            child.Resolve<IDisposable>();
        }
dadhi commented 1 year ago

@dvabuzyarov Appreciated :)

dadhi commented 1 year ago

@dvabuzyarov The fix is published to NuGet with DryIoc v5.3.2