Browse Source

Fix race condition when searching for NuGet packages.

pull/419/head
Matt Ward 11 years ago
parent
commit
5da67914a4
  1. 1
      src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj
  2. 2
      src/AddIns/Misc/PackageManagement/Project/Src/AvailablePackagesViewModel.cs
  3. 32
      src/AddIns/Misc/PackageManagement/Project/Src/PackagesForSelectedPageQuery.cs
  4. 8
      src/AddIns/Misc/PackageManagement/Project/Src/PackagesForSelectedPageResult.cs
  5. 57
      src/AddIns/Misc/PackageManagement/Project/Src/PackagesViewModel.cs
  6. 6
      src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeTaskFactory.cs
  7. 3
      src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestablePackagesViewModel.cs
  8. 19
      src/AddIns/Misc/PackageManagement/Test/Src/PackagesViewModelTests.cs

1
src/AddIns/Misc/PackageManagement/Project/PackageManagement.csproj

@ -232,6 +232,7 @@
<Compile Include="Src\PackageManagementServiceProvider.cs" /> <Compile Include="Src\PackageManagementServiceProvider.cs" />
<Compile Include="Src\IPackageRepositoryExtensions.cs" /> <Compile Include="Src\IPackageRepositoryExtensions.cs" />
<Compile Include="Src\PackageRepositoryFactoryEventArgs.cs" /> <Compile Include="Src\PackageRepositoryFactoryEventArgs.cs" />
<Compile Include="Src\PackagesForSelectedPageQuery.cs" />
<Compile Include="Src\ParentPackagesOperationEventArgs.cs" /> <Compile Include="Src\ParentPackagesOperationEventArgs.cs" />
<Compile Include="Src\ProjectBuilder.cs" /> <Compile Include="Src\ProjectBuilder.cs" />
<Compile Include="Src\ProjectRootElementExtensions.cs" /> <Compile Include="Src\ProjectRootElementExtensions.cs" />

2
src/AddIns/Misc/PackageManagement/Project/Src/AvailablePackagesViewModel.cs

@ -84,7 +84,7 @@ namespace ICSharpCode.PackageManagement
.Where(package => package.IsAbsoluteLatestVersion); .Where(package => package.IsAbsoluteLatestVersion);
} }
return repository return repository
.Search(SearchTerms, IncludePrerelease) .Search(searchCriteria, IncludePrerelease)
.Where(package => package.IsLatestVersion); .Where(package => package.IsLatestVersion);
} }

32
src/AddIns/Misc/PackageManagement/Project/Src/PackagesForSelectedPageQuery.cs

@ -0,0 +1,32 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using NuGet;
namespace ICSharpCode.PackageManagement
{
public class PackagesForSelectedPageQuery
{
public PackagesForSelectedPageQuery (
PackagesViewModel viewModel,
IEnumerable<IPackage> allPackages,
string searchCriteria)
{
Skip = viewModel.ItemsBeforeFirstPage;
Take = viewModel.PageSize;
AllPackages = allPackages;
SearchCriteria = searchCriteria;
TotalPackages = viewModel.TotalItems;
}
public int Skip { get; private set; }
public int Take { get; private set; }
public string SearchCriteria { get; private set; }
public int TotalPackages { get; set; }
public IEnumerable<IPackage> AllPackages { get; set; }
}
}

8
src/AddIns/Misc/PackageManagement/Project/Src/PackagesForSelectedPageResult.cs

@ -26,15 +26,19 @@ namespace ICSharpCode.PackageManagement
{ {
public class PackagesForSelectedPageResult public class PackagesForSelectedPageResult
{ {
public PackagesForSelectedPageResult(IEnumerable<IPackage> packages, int totalPackages) public PackagesForSelectedPageResult(IEnumerable<IPackage> packages, PackagesForSelectedPageQuery query)
{ {
this.Packages = packages; this.Packages = packages;
this.TotalPackagesOnPage = packages.Count(); this.TotalPackagesOnPage = packages.Count();
this.TotalPackages = totalPackages; this.TotalPackages = query.TotalPackages;
this.AllPackages = query.AllPackages;
this.Query = query;
} }
public PackagesForSelectedPageQuery Query { get; set; }
public IEnumerable<IPackage> Packages { get; set; } public IEnumerable<IPackage> Packages { get; set; }
public int TotalPackagesOnPage { get; set; } public int TotalPackagesOnPage { get; set; }
public int TotalPackages { get; set; } public int TotalPackages { get; set; }
public IEnumerable<IPackage> AllPackages { get; set; }
} }
} }

57
src/AddIns/Misc/PackageManagement/Project/Src/PackagesViewModel.cs

@ -41,6 +41,7 @@ namespace ICSharpCode.PackageManagement
IEnumerable<IPackage> allPackages; IEnumerable<IPackage> allPackages;
ITask<PackagesForSelectedPageResult> task; ITask<PackagesForSelectedPageResult> task;
bool includePrerelease; bool includePrerelease;
PackagesForSelectedPageQuery packagesForSelectedPageQuery;
public PackagesViewModel( public PackagesViewModel(
IPackageManagementSolution solution, IPackageManagementSolution solution,
@ -153,15 +154,18 @@ namespace ICSharpCode.PackageManagement
void CreateReadPackagesTask() void CreateReadPackagesTask()
{ {
var query = new PackagesForSelectedPageQuery(this, allPackages, GetSearchCriteria());
packagesForSelectedPageQuery = query;
task = taskFactory.CreateTask( task = taskFactory.CreateTask(
() => GetPackagesForSelectedPageResult(), () => GetPackagesForSelectedPageResult(query),
OnPackagesReadForSelectedPage); OnPackagesReadForSelectedPage);
} }
PackagesForSelectedPageResult GetPackagesForSelectedPageResult() PackagesForSelectedPageResult GetPackagesForSelectedPageResult(PackagesForSelectedPageQuery query)
{ {
IEnumerable<IPackage> packages = GetPackagesForSelectedPage(); IEnumerable<IPackage> packages = GetPackagesForSelectedPage(query);
return new PackagesForSelectedPageResult(packages, TotalItems); return new PackagesForSelectedPageResult(packages, query);
} }
void OnPackagesReadForSelectedPage(ITask<PackagesForSelectedPageResult> task) void OnPackagesReadForSelectedPage(ITask<PackagesForSelectedPageResult> task)
@ -171,12 +175,19 @@ namespace ICSharpCode.PackageManagement
SaveError(task.Exception); SaveError(task.Exception);
} else if (task.IsCancelled) { } else if (task.IsCancelled) {
// Ignore // Ignore
} else if (!IsCurrentQuery(task.Result)) {
// Ignore.
} else { } else {
UpdatePackagesForSelectedPage(task.Result); UpdatePackagesForSelectedPage(task.Result);
} }
base.OnPropertyChanged(null); base.OnPropertyChanged(null);
} }
bool IsCurrentQuery(PackagesForSelectedPageResult result)
{
return packagesForSelectedPageQuery == result.Query;
}
void SaveError(AggregateException ex) void SaveError(AggregateException ex)
{ {
HasError = true; HasError = true;
@ -194,6 +205,8 @@ namespace ICSharpCode.PackageManagement
{ {
pages.TotalItems = result.TotalPackages; pages.TotalItems = result.TotalPackages;
pages.TotalItemsOnSelectedPage = result.TotalPackagesOnPage; pages.TotalItemsOnSelectedPage = result.TotalPackagesOnPage;
TotalItems = result.TotalPackages;
allPackages = result.AllPackages;
UpdatePackageViewModels(result.Packages); UpdatePackageViewModels(result.Packages);
} }
@ -203,20 +216,20 @@ namespace ICSharpCode.PackageManagement
base.OnPropertyChanged(null); base.OnPropertyChanged(null);
} }
IEnumerable<IPackage> GetPackagesForSelectedPage() IEnumerable<IPackage> GetPackagesForSelectedPage(PackagesForSelectedPageQuery query)
{ {
IEnumerable<IPackage> filteredPackages = GetFilteredPackagesBeforePagingResults(); IEnumerable<IPackage> filteredPackages = GetFilteredPackagesBeforePagingResults(query);
return GetPackagesForSelectedPage(filteredPackages); return GetPackagesForSelectedPage(filteredPackages, query);
} }
IEnumerable<IPackage> GetFilteredPackagesBeforePagingResults() IEnumerable<IPackage> GetFilteredPackagesBeforePagingResults(PackagesForSelectedPageQuery query)
{ {
if (allPackages == null) { if (query.AllPackages == null) {
IQueryable<IPackage> packages = GetPackagesFromPackageSource(); IQueryable<IPackage> packages = GetPackagesFromPackageSource(query.SearchCriteria);
TotalItems = packages.Count(); query.TotalPackages = packages.Count();
allPackages = GetFilteredPackagesBeforePagingResults(packages); query.AllPackages = GetFilteredPackagesBeforePagingResults(packages);
} }
return allPackages; return query.AllPackages;
} }
/// <summary> /// <summary>
@ -224,7 +237,12 @@ namespace ICSharpCode.PackageManagement
/// </summary> /// </summary>
public IQueryable<IPackage> GetPackagesFromPackageSource() public IQueryable<IPackage> GetPackagesFromPackageSource()
{ {
IQueryable<IPackage> packages = GetAllPackages(GetSearchCriteria()); return GetPackagesFromPackageSource(GetSearchCriteria());
}
IQueryable<IPackage> GetPackagesFromPackageSource(string searchCriteria)
{
IQueryable<IPackage> packages = GetAllPackages(searchCriteria);
return OrderPackages(packages); return OrderPackages(packages);
} }
@ -242,12 +260,11 @@ namespace ICSharpCode.PackageManagement
return SearchTerms; return SearchTerms;
} }
IEnumerable<IPackage> GetPackagesForSelectedPage(IEnumerable<IPackage> allPackages) IEnumerable<IPackage> GetPackagesForSelectedPage(IEnumerable<IPackage> allPackages, PackagesForSelectedPageQuery query)
{ {
int packagesToSkip = pages.ItemsBeforeFirstPage;
return allPackages return allPackages
.Skip(packagesToSkip) .Skip(query.Skip)
.Take(pages.PageSize); .Take(query.Take);
} }
/// <summary> /// <summary>
@ -325,6 +342,10 @@ namespace ICSharpCode.PackageManagement
set { pages.PageSize = value; } set { pages.PageSize = value; }
} }
public int ItemsBeforeFirstPage {
get { return pages.ItemsBeforeFirstPage; }
}
public bool IsPaged { public bool IsPaged {
get { return pages.IsPaged; } get { return pages.IsPaged; }
} }

6
src/AddIns/Misc/PackageManagement/Test/Src/Helpers/FakeTaskFactory.cs

@ -51,6 +51,12 @@ namespace PackageManagement.Tests.Helpers
} }
} }
public void ExecuteTask(int index)
{
var task = FakeTasksCreated[index] as FakeTask<PackagesForSelectedPageResult>;
task.ExecuteTaskCompletely();
}
public void ClearAllFakeTasks() public void ClearAllFakeTasks()
{ {
FakeTasksCreated.Clear(); FakeTasksCreated.Clear();

3
src/AddIns/Misc/PackageManagement/Test/Src/Helpers/TestablePackagesViewModel.cs

@ -64,10 +64,11 @@ namespace PackageManagement.Tests.Helpers
AddFakePackage("Test"); AddFakePackage("Test");
} }
public void AddFakePackage(string packageId) public FakePackage AddFakePackage(string packageId)
{ {
FakePackage package = CreateFakePackage(packageId); FakePackage package = CreateFakePackage(packageId);
FakePackages.Add(package); FakePackages.Add(package);
return package;
} }
FakePackage CreateFakePackage(string packageId) FakePackage CreateFakePackage(string packageId)

19
src/AddIns/Misc/PackageManagement/Test/Src/PackagesViewModelTests.cs

@ -333,22 +333,20 @@ namespace PackageManagement.Tests
} }
[Test] [Test]
public void ReadPackages_RepositoryHasThreePackagesWhenSelectedPageIsOneAndPageSizeIsTwo_TwoPackageViewModelsCreatedForFirstTwoPackages() public void ReadPackages_SecondQueryFinishesBeforeFirst_PackagesInViewModelAreForSecondQuery()
{ {
CreateViewModel(); CreateViewModel();
viewModel.PageSize = 2;
viewModel.SelectedPageNumber = 1;
viewModel.AddThreeFakePackages(); viewModel.AddThreeFakePackages();
FakePackage package = viewModel.AddFakePackage("MyTest");
viewModel.ReadPackages(); viewModel.ReadPackages();
CompleteReadPackagesTask(); viewModel.SearchTerms = "MyTest";
var expectedPackages = new List<FakePackage>(); var expectedPackages = new FakePackage [] { package };
expectedPackages.Add(viewModel.FakePackages[0]);
expectedPackages.Add(viewModel.FakePackages[1]);
ClearReadPackagesTasks();
viewModel.ReadPackages(); viewModel.ReadPackages();
CompleteReadPackagesTask(); taskFactory.ExecuteTask(1);
taskFactory.ExecuteTask(0);
ClearReadPackagesTasks();
PackageCollectionAssert.AreEqual(expectedPackages, viewModel.PackageViewModels); PackageCollectionAssert.AreEqual(expectedPackages, viewModel.PackageViewModels);
} }
@ -1072,7 +1070,8 @@ namespace PackageManagement.Tests
CreateViewModel(); CreateViewModel();
viewModel.AddSixFakePackages(); viewModel.AddSixFakePackages();
viewModel.ReadPackages(); viewModel.ReadPackages();
taskFactory.FirstFakeTaskCreated.Result = new PackagesForSelectedPageResult(viewModel.FakePackages, 6); var query = new PackagesForSelectedPageQuery(viewModel, null, null);
taskFactory.FirstFakeTaskCreated.Result = new PackagesForSelectedPageResult(viewModel.FakePackages, query);
taskFactory.FirstFakeTaskCreated.IsFaulted = true; taskFactory.FirstFakeTaskCreated.IsFaulted = true;
CompleteReadPackagesTask(); CompleteReadPackagesTask();

Loading…
Cancel
Save