Make PublishedContentQueryAccessor usable (#11601)

* Make PublishedContentQueryAccessor usable

Closes #11319

* Make xmldocs for IPublishedContentQueryAccessor more helpful.
This commit is contained in:
Paul Johnson
2021-11-15 13:23:02 +00:00
committed by GitHub
parent 5d19c663cd
commit d147d7d412
7 changed files with 105 additions and 6 deletions

View File

@@ -0,0 +1,19 @@
using System;
namespace Umbraco.Cms.Core.DependencyInjection
{
/// <summary>
/// Provides access to a request scoped service provider when available for cases where
/// IHttpContextAccessor is not available. e.g. No reference to AspNetCore.Http in core.
/// </summary>
public interface IScopedServiceProvider
{
/// <summary>
/// Gets a request scoped service provider when available.
/// </summary>
/// <remarks>
/// Can be null.
/// </remarks>
IServiceProvider ServiceProvider { get; }
}
}

View File

@@ -166,7 +166,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
builder.Services.AddScoped<ITagQuery, TagQuery>();
builder.Services.AddSingleton<IUmbracoTreeSearcherFields, UmbracoTreeSearcherFields>();
builder.Services.AddSingleton<IPublishedContentQueryAccessor, PublishedContentQueryAccessor>();
builder.Services.AddSingleton<IPublishedContentQueryAccessor, PublishedContentQueryAccessor>(sp =>
new PublishedContentQueryAccessor(sp.GetRequiredService<IScopedServiceProvider>())
);
builder.Services.AddScoped<IPublishedContentQuery>(factory =>
{
var umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();

View File

@@ -1,7 +1,18 @@
using Umbraco.Cms.Infrastructure;
namespace Umbraco.Cms.Core
{
/// <remarks>
/// Not intended for use in background threads where you should make use of <see cref="Umbraco.Cms.Core.Web.IUmbracoContextFactory.EnsureUmbracoContext"/>
/// and instead resolve IPublishedContentQuery from a <see cref="Microsoft.Extensions.DependencyInjection.IServiceScope"/>
/// e.g. using <see cref="Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.CreateScope"/>
/// <example>
/// <code>
/// // Background thread example
/// using UmbracoContextReference _ = _umbracoContextFactory.EnsureUmbracoContext();
/// using IServiceScope serviceScope = _serviceProvider.CreateScope();
/// IPublishedContentQuery query = serviceScope.ServiceProvider.GetRequiredService&lt;IPublishedContentQuery&gt;();
/// </code>
/// </example>
/// </remarks>
public interface IPublishedContentQueryAccessor
{
bool TryGetValue(out IPublishedContentQuery publishedContentQuery);

View File

@@ -1,17 +1,21 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
namespace Umbraco.Cms.Core
{
public class PublishedContentQueryAccessor : IPublishedContentQueryAccessor
{
private readonly IServiceProvider _serviceProvider;
private readonly IScopedServiceProvider _scopedServiceProvider;
public PublishedContentQueryAccessor(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
[Obsolete("Please use alternative constructor")]
public PublishedContentQueryAccessor(IServiceProvider serviceProvider) => _scopedServiceProvider = serviceProvider.GetRequiredService<IScopedServiceProvider>();
public PublishedContentQueryAccessor(IScopedServiceProvider scopedServiceProvider) => _scopedServiceProvider = scopedServiceProvider;
public bool TryGetValue(out IPublishedContentQuery publishedContentQuery)
{
publishedContentQuery = _serviceProvider.GetRequiredService<IPublishedContentQuery>();
publishedContentQuery = _scopedServiceProvider.ServiceProvider?.GetService<IPublishedContentQuery>();
return publishedContentQuery is not null;
}

View File

@@ -0,0 +1,17 @@
using System;
using Microsoft.AspNetCore.Http;
using Umbraco.Cms.Core.DependencyInjection;
namespace Umbraco.Cms.Web.Common.DependencyInjection
{
/// <inheritdoc />
internal class ScopedServiceProvider : IScopedServiceProvider
{
private readonly IHttpContextAccessor _accessor;
public ScopedServiceProvider(IHttpContextAccessor accessor) => _accessor = accessor;
/// <inheritdoc />
public IServiceProvider ServiceProvider => _accessor.HttpContext?.RequestServices;
}
}

View File

@@ -349,6 +349,7 @@ namespace Umbraco.Extensions
builder.Services.AddSingleton<ContentModelBinder>();
builder.Services.AddSingleton<IUmbracoHelperAccessor, UmbracoHelperAccessor>();
builder.Services.AddSingleton<IScopedServiceProvider, ScopedServiceProvider>();
builder.Services.AddScoped<UmbracoHelper>();
builder.Services.AddScoped<IBackOfficeSecurity, BackOfficeSecurity>();

View File

@@ -0,0 +1,45 @@
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Tests.Integration.TestServerTest;
namespace Umbraco.Cms.Tests.Integration.Umbraco.Core
{
[TestFixture]
public class PublishedContentQueryAccessorTests : UmbracoTestServerTestBase
{
[Test]
public async Task PublishedContentQueryAccessor_WithRequestScope_WillProvideQuery()
{
HttpResponseMessage result = await Client.GetAsync("/demo-published-content-query-accessor");
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
}
}
public class PublishedContentQueryAccessorTestController : Controller
{
private readonly IPublishedContentQueryAccessor _accessor;
public PublishedContentQueryAccessorTestController(IPublishedContentQueryAccessor accessor)
{
_accessor = accessor;
}
[HttpGet("demo-published-content-query-accessor")]
public IActionResult Test()
{
var success = _accessor.TryGetValue(out IPublishedContentQuery query);
if (!success || query == null)
{
throw new ApplicationException("It doesn't work");
}
return Ok();
}
}
}