Skip to content

Commit e738574

Browse files
authored
Merge pull request #8107 from NuGet/dev
[ReleasePrep][2020.07.14]RI dev into master
2 parents 5ae9343 + e6d6dc8 commit e738574

39 files changed

Lines changed: 10002 additions & 6429 deletions

.github/CODEOWNERS

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
# the repo. Unless a later match takes precedence,
33
# review when someone opens a pull request.
44
# For more on how to customize the CODEOWNERS file - https://help.github.com/en/articles/about-code-owners
5-
* @agr @cristinamanum @dannyjdev @joelverhagen @loic-sharma @ryuyu @scottbommarito @skofman1 @shishirx34 @xavierdecoster @zhhyu
6-
5+
* @agr @cristinamanum @dannyjdev @drewgillies @joelverhagen @loic-sharma @lyndaidaii @ryuyu @skofman1 @shishirx34 @xavierdecoster @zhhyu

src/NuGetGallery.Core/Entities/EntitiesContext.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,18 @@ public EntitiesContext(DbConnection connection, bool readOnly)
5656
ReadOnly = readOnly;
5757
}
5858

59+
public IDisposable WithQueryHint(string queryHint)
60+
{
61+
if (QueryHint != null)
62+
{
63+
throw new InvalidOperationException("A query hint is already applied.");
64+
}
65+
66+
return new QueryHintScope(this, queryHint);
67+
}
68+
5969
public bool ReadOnly { get; private set; }
70+
public string QueryHint { get; private set; }
6071
public DbSet<Package> Packages { get; set; }
6172
public DbSet<PackageDeprecation> Deprecations { get; set; }
6273
public DbSet<PackageRegistration> PackageRegistrations { get; set; }
@@ -503,5 +514,21 @@ protected override void OnModelCreating(DbModelBuilder modelBuilder)
503514
.WillCascadeOnDelete(false);
504515
}
505516
#pragma warning restore 618
517+
518+
private class QueryHintScope : IDisposable
519+
{
520+
private readonly EntitiesContext _entitiesContext;
521+
522+
public QueryHintScope(EntitiesContext entitiesContext, string queryHint)
523+
{
524+
_entitiesContext = entitiesContext ?? throw new ArgumentNullException(nameof(entitiesContext));
525+
_entitiesContext.QueryHint = queryHint;
526+
}
527+
528+
public void Dispose()
529+
{
530+
_entitiesContext.QueryHint = null;
531+
}
532+
}
506533
}
507534
}

src/NuGetGallery.Core/Entities/IReadOnlyEntitiesContext.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,19 @@ public interface IReadOnlyEntitiesContext : IDisposable
1414
DbSet<T> Set<T>() where T : class;
1515

1616
void SetCommandTimeout(int? seconds);
17+
18+
/// <summary>
19+
/// Sets the <see cref="QueryHint"/> for queries to the provided value until the returned disposable is disposed.
20+
/// Note that this method MUST NOT be called with user input.
21+
/// </summary>
22+
/// <param name="queryHint">The query hint.</param>
23+
/// <example>Provide "RECOMPILE" to disable query plan caching.</example>
24+
/// <returns>A disposable that determines the lifetime of the provided query hint.</returns>
25+
IDisposable WithQueryHint(string queryHint);
26+
27+
/// <summary>
28+
/// The current query hint to use for queries. Can be set using <see cref="WithQueryHint(string)"/>.
29+
/// </summary>
30+
string QueryHint { get; }
1731
}
1832
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Data.Common;
5+
using System.Data.Entity.Infrastructure.Interception;
6+
using System.Diagnostics.CodeAnalysis;
7+
8+
namespace NuGetGallery
9+
{
10+
/// <summary>
11+
/// A global (static) interceptor for Entity Framework queries. This is used for
12+
/// <see cref="IReadOnlyEntitiesContext.WithQueryHint(string)"/> to set a query hint for a short window of time on
13+
/// a specific entity context. Inspired by: https://stackoverflow.com/a/45170243
14+
/// </summary>
15+
public class QueryHintInterceptor : DbCommandInterceptor
16+
{
17+
[SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The query hint is not customer input.")]
18+
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
19+
{
20+
foreach (var dbContext in interceptionContext.DbContexts)
21+
{
22+
if (dbContext is IReadOnlyEntitiesContext entitiesContext)
23+
{
24+
var queryHint = entitiesContext.QueryHint;
25+
if (queryHint != null)
26+
{
27+
command.CommandText += $" OPTION ( {queryHint} )";
28+
}
29+
30+
break;
31+
}
32+
}
33+
34+
base.ReaderExecuting(command, interceptionContext);
35+
}
36+
}
37+
}

src/NuGetGallery.Core/Entities/ReadOnlyEntitiesContext.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public DbSet<Package> Packages
3030
}
3131
}
3232

33+
public string QueryHint => _entitiesContext.QueryHint;
34+
3335
DbSet<T> IReadOnlyEntitiesContext.Set<T>()
3436
{
3537
return _entitiesContext.Set<T>();
@@ -44,5 +46,7 @@ public void Dispose()
4446
{
4547
_entitiesContext.Dispose();
4648
}
49+
50+
public IDisposable WithQueryHint(string queryHint) => _entitiesContext.WithQueryHint(queryHint);
4751
}
4852
}

src/NuGetGallery.Core/NuGetGallery.Core.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
<Compile Include="DisposableAction.cs" />
122122
<Compile Include="Entities\DatabaseWrapper.cs" />
123123
<Compile Include="Entities\DbContextTransactionWrapper.cs" />
124+
<Compile Include="Entities\QueryHintInterceptor.cs" />
124125
<Compile Include="Entities\ReadOnlyEntitiesContext.cs" />
125126
<Compile Include="Entities\IReadOnlyEntitiesContext.cs" />
126127
<Compile Include="Entities\ReadOnlyEntityRepository.cs" />

src/NuGetGallery.Services/Configuration/FeatureFlagService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class FeatureFlagService : IFeatureFlagService
2020
private const string EmbeddedIconFlightName = GalleryPrefix + "EmbeddedIcons";
2121
private const string ForceFlatContainerIconsFeatureName = GalleryPrefix + "ForceFlatContainerIcons";
2222
private const string GitHubUsageFlightName = GalleryPrefix + "GitHubUsage";
23+
private const string AdvancedSearchFlightName = GalleryPrefix + "AdvancedSearch";
2324
private const string PackageDependentsFlightName = GalleryPrefix + "PackageDependents";
2425
private const string ManageDeprecationFeatureName = GalleryPrefix + "ManageDeprecation";
2526
private const string ManageDeprecationForManyVersionsFeatureName = GalleryPrefix + "ManageDeprecationMany";
@@ -136,6 +137,11 @@ public bool IsGitHubUsageEnabled(User user)
136137
return _client.IsEnabled(GitHubUsageFlightName, user, defaultValue: false);
137138
}
138139

140+
public bool IsAdvancedSearchEnabled(User user)
141+
{
142+
return _client.IsEnabled(AdvancedSearchFlightName, user, defaultValue: false);
143+
}
144+
139145
public bool IsPackageDependentsEnabled(User user)
140146
{
141147
return _client.IsEnabled(PackageDependentsFlightName, user, defaultValue: false);

src/NuGetGallery.Services/Configuration/IFeatureFlagService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ public interface IFeatureFlagService
8080
/// <returns>Whether or not the Flight is enabled for the user</returns>
8181
bool IsGitHubUsageEnabled(User user);
8282

83+
/// <summary>
84+
/// Whether a user can see the "Advanced Search" panel in the packages list view as well as use the
85+
/// advanced search request parameters
86+
/// </summary>
87+
/// <param name="user">The user to test for the Flight</param>
88+
/// <returns>Whether or not the Flight is enabled for the user</returns>
89+
bool IsAdvancedSearchEnabled(User user);
90+
8391
/// <summary>
8492
/// Whether a user can see the "Package Dependents" section in a package's display page. Also, no
8593
/// data is gathered from the database

src/NuGetGallery.Services/PackageManagement/PackageService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,13 @@ public PackageDependents GetPackageDependents(string id)
155155
}
156156

157157
PackageDependents result = new PackageDependents();
158-
result.TopPackages = GetListOfDependents(id);
159-
result.TotalPackageCount = GetDependentCount(id);
158+
159+
using (_entitiesContext.WithQueryHint("RECOMPILE"))
160+
{
161+
result.TopPackages = GetListOfDependents(id);
162+
result.TotalPackageCount = GetDependentCount(id);
163+
}
164+
160165
return result;
161166
}
162167

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"PreviewSearchPercentage": 0,
33
"PreviewHijackPercentage": 0,
4-
"DependentsPercentage": 0
4+
"DependentsPercentage": 100
55
}

0 commit comments

Comments
 (0)