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);
}
}