/// <summary>
        /// Dependency walk
        /// </summary>
        public override async Task <IEnumerable <PackageDependencyInfo> > ResolvePackages(IEnumerable <PackageIdentity> packages, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token)
        {
            if (projectFramework == null)
            {
                throw new ArgumentNullException("projectFramework");
            }

            if (packages == null)
            {
                throw new ArgumentNullException("packages");
            }

            HashSet <PackageDependencyInfo> results = new HashSet <PackageDependencyInfo>(PackageIdentityComparer.Default);

            foreach (PackageIdentity package in packages)
            {
                VersionRange range = package.HasVersion ? new VersionRange(package.Version, true, package.Version, true) : VersionRange.All;

                var target = new NuGet.Packaging.Core.PackageDependency(package.Id, range);

                results.UnionWith(await Seek(target, projectFramework, includePrerelease, Enumerable.Empty <string>(), token));
            }

            // pre-release should not be in the final set, but filter again just to be sure
            return(results.Where(e => includePrerelease || !e.Version.IsPrerelease));
        }
        // Thread safe fetch for the target only, no child dependencies
        private async Task Ensure(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token)
        {
            object lockObj = _lockObjsById.GetOrAdd(target.Id, new object());

            // lock per package Id
            lock (lockObj)
            {
                VersionRange alreadySearched = null;

                // Fetch by range is no longer supported, all packages will be fetched
                if (!_rangeSearched.TryGetValue(target.Id, out alreadySearched))
                {
                    // server search
                    IEnumerable <IPackage> repoPackages = V2Client.FindPackagesById(target.Id);

                    _rangeSearched.AddOrUpdate(target.Id, VersionRange.All, (k, v) => VersionRange.All);

                    HashSet <PackageDependencyInfo> foundPackages = null;

                    // add everything to found
                    if (!_found.TryGetValue(target.Id, out foundPackages))
                    {
                        foundPackages = new HashSet <PackageDependencyInfo>(PackageIdentity.Comparer);
                        _found.TryAdd(target.Id, foundPackages);
                    }

                    // add current packages to found
                    IEnumerable <PackageDependencyInfo> packageVersions = repoPackages.Select(p => CreateDependencyInfo(p, projectFramework));
                    foundPackages.UnionWith(packageVersions);
                }
            }
        }
        // Thread safe retrieval of fetched packages for the target only, no child dependencies
        private IEnumerable <PackageDependencyInfo> Get(NuGet.Packaging.Core.PackageDependency target, bool includePrerelease)
        {
            HashSet <PackageDependencyInfo> packages = null;

            if (!_found.TryGetValue(target.Id, out packages))
            {
                return(Enumerable.Empty <PackageDependencyInfo>());
            }
            else
            {
                object lockObj = _lockObjsById.GetOrAdd(target.Id, new object());

                // lock per package Id
                lock (lockObj)
                {
                    return(packages.Where(p => includePrerelease || !p.Version.IsPrerelease).Where(p => target.VersionRange.Satisfies(p.Version)));
                }
            }
        }
        /// <summary>
        /// Recursive package dependency info gather
        /// </summary>
        private async Task <IEnumerable <PackageDependencyInfo> > Seek(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, IEnumerable <string> parents, CancellationToken token)
        {
            // check if we are cancelled
            token.ThrowIfCancellationRequested();

            List <PackageDependencyInfo> results = new List <PackageDependencyInfo>();

            // circular dependency check protection
            if (!parents.Contains(target.Id, StringComparer.OrdinalIgnoreCase))
            {
                await Ensure(target, projectFramework, includePrerelease, token);

                var packages = Get(target, includePrerelease);

                results.AddRange(packages);

                // combine all version ranges found for an id into a single range
                var toSeek = packages.SelectMany(g => g.Dependencies).GroupBy(d => d.Id, StringComparer.OrdinalIgnoreCase)
                             .OrderBy(d => d.Key)
                             .Select(g => new NuGet.Packaging.Core.PackageDependency(g.Key, VersionRange.Combine(g.Select(d => d.VersionRange))));

                // recurse
                Stack <Task <IEnumerable <PackageDependencyInfo> > > tasks = new Stack <Task <IEnumerable <PackageDependencyInfo> > >();

                foreach (NuGet.Packaging.Core.PackageDependency dep in toSeek)
                {
                    // run tasks on another thread
                    var task = Task.Run(async() => await Seek(dep, projectFramework, includePrerelease, parents.Concat(new string[] { target.Id }), token));
                    tasks.Push(task);
                }

                // add child dep results
                foreach (var task in tasks)
                {
                    results.AddRange(await task);
                }
            }

            return(results);
        }
 private async Task <IEnumerable <NuGetVersion> > CacheGetAllVersionsOfDependency(NuGetFactory factory,
                                                                                  NuGet.Packaging.Core.PackageDependency dependency)
 {
     return(await CacheGetAllVersionsOfPackage(factory, dependency.Id));
 }
        /// <summary>
        /// Dependency walk
        /// </summary>
        public override async Task<IEnumerable<PackageDependencyInfo>> ResolvePackages(IEnumerable<PackageIdentity> packages, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token)
        {
            if (projectFramework == null)
            {
                throw new ArgumentNullException("projectFramework");
            }

            if (packages == null)
            {
                throw new ArgumentNullException("packages");
            }

            HashSet<PackageDependencyInfo> results = new HashSet<PackageDependencyInfo>(PackageIdentityComparer.Default);

            foreach (PackageIdentity package in packages)
            {
                VersionRange range = package.HasVersion ? new VersionRange(package.Version, true, package.Version, true) : VersionRange.All;

                var target = new NuGet.Packaging.Core.PackageDependency(package.Id, range);

                results.UnionWith(await Seek(target, projectFramework, includePrerelease, Enumerable.Empty<string>(), token));
            }

            // pre-release should not be in the final set, but filter again just to be sure
            return results.Where(e => includePrerelease || !e.Version.IsPrerelease);
        }
        // Thread safe fetch for the target only, no child dependencies
        private async Task Ensure(NuGet.Packaging.Core.PackageDependency target, NuGetFramework projectFramework, bool includePrerelease, CancellationToken token)
        {
            object lockObj = _lockObjsById.GetOrAdd(target.Id, new object());

            // lock per package Id
            lock (lockObj)
            {
                VersionRange alreadySearched = null;

                if (!_rangeSearched.TryGetValue(target.Id, out alreadySearched))
                {
                    alreadySearched = EmptyRange;
                }

                if (alreadySearched == null || !target.VersionRange.IsSubSetOrEqualTo(alreadySearched))
                {
                    // find what we haven't checked already
                    var needed = NeededRange(alreadySearched, target.VersionRange);

                    // adjust prerelease, is this needed?
                    needed = ModifyRange(needed, includePrerelease);

                    if (!_versionRangeComparer.Equals(needed, EmptyRange))
                    {
                        // server search
                        IEnumerable <IPackage> repoPackages = null;

                        if (_useFindById)
                        {
                            // Ranges fail in some cases for local repos, to work around this just collect every
                            // version of the package to filter later
                            repoPackages = V2Client.FindPackagesById(target.Id);
                        }
                        else
                        {
                            // DataService Repository
                            repoPackages = V2Client.FindPackages(target.Id, GetVersionSpec(needed), includePrerelease, false);
                        }

                        List <VersionRange> currentRanges = new List <VersionRange>();
                        currentRanges.Add(target.VersionRange);

                        if (alreadySearched != null)
                        {
                            currentRanges.Add(alreadySearched);
                        }

                        // update the already searched range
                        VersionRange combined = null;

                        if (_useFindById)
                        {
                            // for local repos we found all possible versions
                            combined = VersionRange.All;
                        }
                        else
                        {
                            // for non-local repos find the real range
                            combined = VersionRange.Combine(currentRanges);
                        }

                        _rangeSearched.AddOrUpdate(target.Id, combined, (k, v) => combined);

                        HashSet <PackageDependencyInfo> foundPackages = null;

                        // add everything to found
                        if (!_found.TryGetValue(target.Id, out foundPackages))
                        {
                            foundPackages = new HashSet <PackageDependencyInfo>(PackageIdentity.Comparer);
                            _found.TryAdd(target.Id, foundPackages);
                        }

                        // add current packages to found
                        IEnumerable <PackageDependencyInfo> packageVersions = repoPackages.Select(p => CreateDependencyInfo(p, projectFramework));
                        foundPackages.UnionWith(packageVersions);
                    }
                }
            }
        }