File: System\DirectoryServices\DirectoryEntries.cs
Web Access
Project: src\src\runtime\src\libraries\System.DirectoryServices\src\System.DirectoryServices.csproj (System.DirectoryServices)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections;
using System.Runtime.InteropServices;

namespace System.DirectoryServices
{
    /// <devdoc>
    /// Contains the children (child entries) of an entry in the Active Directory.
    /// </devdoc>
    public class DirectoryEntries : IEnumerable
    {
        // the parent of the children in this collection
        private readonly DirectoryEntry _container;

        internal DirectoryEntries(DirectoryEntry parent)
        {
            _container = parent;
        }

        /// <devdoc>
        /// Gets the schemas that specify which children are shown.
        /// </devdoc>
        public SchemaNameCollection SchemaFilter
        {
            get
            {
                CheckIsContainer();
                SchemaNameCollection.FilterDelegateWrapper filter = new SchemaNameCollection.FilterDelegateWrapper(_container.ContainerObject);
                return new SchemaNameCollection(filter.Getter, filter.Setter);
            }
        }

        private void CheckIsContainer()
        {
            if (!_container.IsContainer)
                throw new InvalidOperationException(SR.Format(SR.DSNotAContainer, _container.Path));
        }

        /// <devdoc>
        /// Creates a request to create a new entry in the container.
        /// </devdoc>
        public DirectoryEntry Add(string name, string schemaClassName)
        {
            CheckIsContainer();
            object newChild = _container.ContainerObject.Create(schemaClassName, name);
            DirectoryEntry entry = new DirectoryEntry(newChild, _container.UsePropertyCache, _container.GetUsername(), _container.GetPassword(), _container.AuthenticationType);
            entry.JustCreated = true;       // suspend writing changes until CommitChanges() is called
            return entry;
        }

        /// <devdoc>
        /// Returns the child with the given name.
        /// </devdoc>
        public DirectoryEntry Find(string name)
        {
            // For IIS: and WinNT: providers  schemaClassName == "" does general search.
            return Find(name, null);
        }

        /// <devdoc>
        /// Returns the child with the given name and of the given type.
        /// </devdoc>
        public DirectoryEntry Find(string name, string? schemaClassName)
        {
            CheckIsContainer();
            // Note: schemaClassName == null does not work for IIS: provider.
            object? o = null;
            try
            {
                o = _container.ContainerObject.GetObject(schemaClassName, name);
            }
            catch (COMException e)
            {
                throw COMExceptionHelper.CreateFormattedComException(e);
            }
            return new DirectoryEntry(o, _container.UsePropertyCache, _container.GetUsername(), _container.GetPassword(), _container.AuthenticationType);
        }

        /// <devdoc>
        /// Deletes a child <see cref='System.DirectoryServices.DirectoryEntry'/> from this collection.
        /// </devdoc>
        public void Remove(DirectoryEntry entry)
        {
            CheckIsContainer();
            try
            {
                _container.ContainerObject.Delete(entry.SchemaClassName, entry.Name);
            }
            catch (COMException e)
            {
                throw COMExceptionHelper.CreateFormattedComException(e);
            }
        }

        public IEnumerator GetEnumerator() => new ChildEnumerator(_container);

        /// <devdoc>
        /// Supports a simple ForEach-style iteration over a collection and defines
        /// enumerators, size, and synchronization methods.
        /// </devdoc>
        private sealed class ChildEnumerator : IEnumerator
        {
            private readonly DirectoryEntry _container;
            private SafeNativeMethods.EnumVariant? _enumVariant;
            private DirectoryEntry? _currentEntry;

            internal ChildEnumerator(DirectoryEntry container)
            {
                _container = container;
                if (container.IsContainer)
                {
                    _enumVariant = new SafeNativeMethods.EnumVariant((SafeNativeMethods.IEnumVariant)container.ContainerObject._NewEnum);
                }
            }

            /// <devdoc>
            /// Gets the current element in the collection.
            /// </devdoc>
            public DirectoryEntry Current
            {
                get
                {
                    if (_enumVariant == null)
                        throw new InvalidOperationException(SR.DSNoCurrentChild);

                    return _currentEntry ??= new DirectoryEntry(_enumVariant.GetValue(), _container.UsePropertyCache, _container.GetUsername(), _container.GetPassword(), _container.AuthenticationType);
                }
            }

            /// <devdoc>
            /// Advances the enumerator to the next element of the collection
            /// and returns a Boolean value indicating whether a valid element is available.
            /// </devdoc>
            public bool MoveNext()
            {
                if (_enumVariant == null)
                    return false;

                _currentEntry = null;
                return _enumVariant.GetNext();
            }

            /// <devdoc>
            /// Resets the enumerator back to its initial position before the first element in the collection.
            /// </devdoc>
            public void Reset()
            {
                if (_enumVariant != null)
                {
                    try
                    {
                        _enumVariant.Reset();
                    }
                    catch (NotImplementedException)
                    {
                        //Some providers might not implement Reset, workaround the problem.
                        _enumVariant = new SafeNativeMethods.EnumVariant((SafeNativeMethods.IEnumVariant)_container.ContainerObject._NewEnum);
                    }
                    _currentEntry = null;
                }
            }

            object IEnumerator.Current => Current;
        }
    }
}