private bool TryUpdate(IEnumerable <IPackage> dependents, ConflictResult conflictResult, IPackage package, out IEnumerable <IPackage> incompatiblePackages) { // Key dependents by id so we can look up the old package later var dependentsLookup = dependents.ToDictionary(d => d.Id, StringComparer.OrdinalIgnoreCase); var compatiblePackages = new Dictionary <IPackage, IPackage>(); // Initialize each compatible package to null foreach (var dependent in dependents) { compatiblePackages[dependent] = null; } // Get compatible packages in one batch so we don't have to make requests for each one var packages = from p in SourceRepository.FindCompatiblePackages(ConstraintProvider, dependentsLookup.Keys, package, TargetFramework, AllowPrereleaseVersions) group p by p.Id into g let oldPackage = dependentsLookup[g.Key] select new { OldPackage = oldPackage, NewPackage = g.Where(p => p.Version > oldPackage.Version) .OrderBy(p => p.Version) .ResolveSafeVersion() }; foreach (var p in packages) { compatiblePackages[p.OldPackage] = p.NewPackage; } // Get all packages that have an incompatibility with the specified package i.e. // We couldn't find a version in the repository that works with the specified package. incompatiblePackages = compatiblePackages.Where(p => p.Value == null) .Select(p => p.Key); if (incompatiblePackages.Any()) { return(false); } IPackageConstraintProvider currentConstraintProvider = ConstraintProvider; try { // Add a constraint for the incoming package so we don't try to update it by mistake. // Scenario: // A 1.0 -> B [1.0] // B 1.0.1, B 1.5, B 2.0 // A 2.0 -> B (any version) // We have A 1.0 and B 1.0 installed. When trying to update to B 1.0.1, we'll end up trying // to find a version of A that works with B 1.0.1. The version in the above case is A 2.0. // When we go to install A 2.0 we need to make sure that when we resolve it's dependencies that we stay within bounds // i.e. when we resolve B for A 2.0 we want to keep the B 1.0.1 we've already chosen instead of trying to grab // B 1.5 or B 2.0. In order to achieve this, we add a constraint for version of B 1.0.1 so we stay within those bounds for B. // Respect all existing constraints plus an additional one that we specify based on the incoming package var constraintProvider = new DefaultConstraintProvider(); constraintProvider.AddConstraint(package.Id, new VersionSpec(package.Version)); ConstraintProvider = new AggregateConstraintProvider(ConstraintProvider, constraintProvider); // Mark the incoming package as visited so that we don't try walking the graph again Marker.MarkVisited(package); var failedPackages = new List <IPackage>(); // Update each of the existing packages to more compatible one foreach (var pair in compatiblePackages) { try { // Remove the old package Uninstall(pair.Key, conflictResult.DependentsResolver, conflictResult.Repository); // Install the new package Walk(pair.Value); } catch { // If we failed to update this package (most likely because of a conflict further up the dependency chain) // we keep track of it so we can report an error about the top level package. failedPackages.Add(pair.Key); } } incompatiblePackages = failedPackages; return(!incompatiblePackages.Any()); } finally { // Restore the current constraint provider ConstraintProvider = currentConstraintProvider; // Mark the package as processing again Marker.MarkProcessing(package); } }