Skip to content

Commit f64894d

Browse files
Paul Johnsonbergmania
authored andcommitted
Make PublishedContentQueryAccessor usable (#11601)
* Make PublishedContentQueryAccessor usable Closes #11319 * Make xmldocs for IPublishedContentQueryAccessor more helpful.
1 parent 32d8e0b commit f64894d

File tree

7 files changed

+105
-6
lines changed

7 files changed

+105
-6
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace Umbraco.Cms.Core.DependencyInjection
4+
{
5+
/// <summary>
6+
/// Provides access to a request scoped service provider when available for cases where
7+
/// IHttpContextAccessor is not available. e.g. No reference to AspNetCore.Http in core.
8+
/// </summary>
9+
public interface IScopedServiceProvider
10+
{
11+
/// <summary>
12+
/// Gets a request scoped service provider when available.
13+
/// </summary>
14+
/// <remarks>
15+
/// Can be null.
16+
/// </remarks>
17+
IServiceProvider ServiceProvider { get; }
18+
}
19+
}

src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,9 @@ public static IUmbracoBuilder AddCoreInitialServices(this IUmbracoBuilder builde
166166
builder.Services.AddScoped<ITagQuery, TagQuery>();
167167

168168
builder.Services.AddSingleton<IUmbracoTreeSearcherFields, UmbracoTreeSearcherFields>();
169-
builder.Services.AddSingleton<IPublishedContentQueryAccessor, PublishedContentQueryAccessor>();
169+
builder.Services.AddSingleton<IPublishedContentQueryAccessor, PublishedContentQueryAccessor>(sp =>
170+
new PublishedContentQueryAccessor(sp.GetRequiredService<IScopedServiceProvider>())
171+
);
170172
builder.Services.AddScoped<IPublishedContentQuery>(factory =>
171173
{
172174
var umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();

src/Umbraco.Infrastructure/IPublishedContentQueryAccessor.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
1-
using Umbraco.Cms.Infrastructure;
2-
31
namespace Umbraco.Cms.Core
42
{
3+
/// <remarks>
4+
/// Not intended for use in background threads where you should make use of <see cref="Umbraco.Cms.Core.Web.IUmbracoContextFactory.EnsureUmbracoContext"/>
5+
/// and instead resolve IPublishedContentQuery from a <see cref="Microsoft.Extensions.DependencyInjection.IServiceScope"/>
6+
/// e.g. using <see cref="Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.CreateScope"/>
7+
/// <example>
8+
/// <code>
9+
/// // Background thread example
10+
/// using UmbracoContextReference _ = _umbracoContextFactory.EnsureUmbracoContext();
11+
/// using IServiceScope serviceScope = _serviceProvider.CreateScope();
12+
/// IPublishedContentQuery query = serviceScope.ServiceProvider.GetRequiredService&lt;IPublishedContentQuery&gt;();
13+
/// </code>
14+
/// </example>
15+
/// </remarks>
516
public interface IPublishedContentQueryAccessor
617
{
718
bool TryGetValue(out IPublishedContentQuery publishedContentQuery);

src/Umbraco.Infrastructure/PublishedContentQueryAccessor.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
using System;
22
using Microsoft.Extensions.DependencyInjection;
3+
using Umbraco.Cms.Core.DependencyInjection;
34

45
namespace Umbraco.Cms.Core
56
{
67
public class PublishedContentQueryAccessor : IPublishedContentQueryAccessor
78
{
8-
private readonly IServiceProvider _serviceProvider;
9+
private readonly IScopedServiceProvider _scopedServiceProvider;
910

10-
public PublishedContentQueryAccessor(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
11+
[Obsolete("Please use alternative constructor")]
12+
public PublishedContentQueryAccessor(IServiceProvider serviceProvider) => _scopedServiceProvider = serviceProvider.GetRequiredService<IScopedServiceProvider>();
13+
14+
public PublishedContentQueryAccessor(IScopedServiceProvider scopedServiceProvider) => _scopedServiceProvider = scopedServiceProvider;
1115

1216
public bool TryGetValue(out IPublishedContentQuery publishedContentQuery)
1317
{
14-
publishedContentQuery = _serviceProvider.GetRequiredService<IPublishedContentQuery>();
18+
publishedContentQuery = _scopedServiceProvider.ServiceProvider?.GetService<IPublishedContentQuery>();
1519

1620
return publishedContentQuery is not null;
1721
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Microsoft.AspNetCore.Http;
3+
using Umbraco.Cms.Core.DependencyInjection;
4+
5+
namespace Umbraco.Cms.Web.Common.DependencyInjection
6+
{
7+
/// <inheritdoc />
8+
internal class ScopedServiceProvider : IScopedServiceProvider
9+
{
10+
private readonly IHttpContextAccessor _accessor;
11+
12+
public ScopedServiceProvider(IHttpContextAccessor accessor) => _accessor = accessor;
13+
14+
/// <inheritdoc />
15+
public IServiceProvider ServiceProvider => _accessor.HttpContext?.RequestServices;
16+
}
17+
}

src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ public static IUmbracoBuilder AddWebComponents(this IUmbracoBuilder builder)
349349
builder.Services.AddSingleton<ContentModelBinder>();
350350

351351
builder.Services.AddSingleton<IUmbracoHelperAccessor, UmbracoHelperAccessor>();
352+
builder.Services.AddSingleton<IScopedServiceProvider, ScopedServiceProvider>();
352353
builder.Services.AddScoped<UmbracoHelper>();
353354
builder.Services.AddScoped<IBackOfficeSecurity, BackOfficeSecurity>();
354355

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Mvc;
6+
using NUnit.Framework;
7+
using Umbraco.Cms.Core;
8+
using Umbraco.Cms.Tests.Integration.TestServerTest;
9+
10+
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core
11+
{
12+
[TestFixture]
13+
public class PublishedContentQueryAccessorTests : UmbracoTestServerTestBase
14+
{
15+
[Test]
16+
public async Task PublishedContentQueryAccessor_WithRequestScope_WillProvideQuery()
17+
{
18+
HttpResponseMessage result = await Client.GetAsync("/demo-published-content-query-accessor");
19+
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
20+
}
21+
}
22+
23+
public class PublishedContentQueryAccessorTestController : Controller
24+
{
25+
private readonly IPublishedContentQueryAccessor _accessor;
26+
27+
public PublishedContentQueryAccessorTestController(IPublishedContentQueryAccessor accessor)
28+
{
29+
_accessor = accessor;
30+
}
31+
32+
[HttpGet("demo-published-content-query-accessor")]
33+
public IActionResult Test()
34+
{
35+
var success = _accessor.TryGetValue(out IPublishedContentQuery query);
36+
37+
if (!success || query == null)
38+
{
39+
throw new ApplicationException("It doesn't work");
40+
}
41+
42+
return Ok();
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)