Skip to content

Commit b2d2d84

Browse files
Merge pull request #65 from umbraco/feature/blobcontainerclientfactory
Use options specific factory to create `BlobContainerClient`
2 parents 7db0211 + c053311 commit b2d2d84

File tree

6 files changed

+119
-6
lines changed

6 files changed

+119
-6
lines changed

examples/Umbraco.StorageProviders.AzureBlob.TestSite/AzureBlobFileSystemComposer.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Azure.Core;
2+
using Azure.Storage.Blobs;
13
using Microsoft.Extensions.Options;
24
using Umbraco.Cms.Core.Composing;
35
using Umbraco.Cms.Core.Events;
@@ -8,10 +10,25 @@ namespace Umbraco.StorageProviders.AzureBlob.TestSite;
810

911
internal sealed class AzureBlobFileSystemComposer : IComposer
1012
{
13+
private static readonly BlobClientOptions _blobClientOptions = new BlobClientOptions
14+
{
15+
Retry = {
16+
Mode = RetryMode.Exponential,
17+
MaxRetries = 3
18+
}
19+
};
20+
1121
public void Compose(IUmbracoBuilder builder)
1222
=> builder
13-
.AddAzureBlobMediaFileSystem()
23+
// Add using default options (overly verbose, but shows how to revert back to the default)
24+
.AddAzureBlobMediaFileSystem(options => options.CreateBlobContainerClientUsingDefault())
25+
// Add using options
26+
.AddAzureBlobMediaFileSystem(options => options.CreateBlobContainerClientUsingOptions(_blobClientOptions))
27+
// If the connection string is parsed to a URI, use the delegate to create a BlobContainerClient
28+
.AddAzureBlobMediaFileSystem(options => options.TryCreateBlobContainerClientUsingUri(uri => new BlobContainerClient(uri, _blobClientOptions)))
29+
// Add the ImageSharp IImageCache implementation using the default media file system and "cache" container root path
1430
.AddAzureBlobImageSharpCache()
31+
// Add notification handler to create the media file system on install if it doesn't exist
1532
.AddNotificationHandler<UnattendedInstallNotification, AzureBlobMediaFileSystemCreateIfNotExistsHandler>();
1633

1734
private sealed class AzureBlobMediaFileSystemCreateIfNotExistsHandler : INotificationHandler<UnattendedInstallNotification>

src/Umbraco.StorageProviders.AzureBlob.ImageSharp/AzureBlobFileSystemImageCache.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ public AzureBlobFileSystemImageCache(IOptionsMonitor<AzureBlobFileSystemOptions>
3131
ArgumentNullException.ThrowIfNull(name);
3232

3333
AzureBlobFileSystemOptions fileSystemOptions = options.Get(name);
34-
_container = new BlobContainerClient(fileSystemOptions.ConnectionString, fileSystemOptions.ContainerName);
34+
_container = fileSystemOptions.CreateBlobContainerClient();
3535
_containerRootPath = GetContainerRootPath(containerRootPath, fileSystemOptions);
3636

3737
options.OnChange((options, changedName) =>
3838
{
3939
if (changedName == name)
4040
{
41-
_container = new BlobContainerClient(options.ConnectionString, options.ContainerName);
41+
_container = options.CreateBlobContainerClient();
4242
_containerRootPath = GetContainerRootPath(containerRootPath, options);
4343
}
4444
});

src/Umbraco.StorageProviders.AzureBlob/AzureBlobFileProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public AzureBlobFileProvider(AzureBlobFileSystemOptions options)
4040
{
4141
ArgumentNullException.ThrowIfNull(options);
4242

43-
_containerClient = new BlobContainerClient(options.ConnectionString, options.ContainerName);
43+
_containerClient = options.CreateBlobContainerClient();
4444
_containerRootPath = options.ContainerRootPath?.Trim(Constants.CharArrays.ForwardSlash);
4545
}
4646

src/Umbraco.StorageProviders.AzureBlob/IO/AzureBlobFileSystem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public sealed class AzureBlobFileSystem : IAzureBlobFileSystem, IFileProviderFac
3030
/// <exception cref="System.ArgumentNullException"><paramref name="ioHelper" /> is <c>null</c>.</exception>
3131
/// <exception cref="System.ArgumentNullException"><paramref name="contentTypeProvider" /> is <c>null</c>.</exception>
3232
public AzureBlobFileSystem(AzureBlobFileSystemOptions options, IHostingEnvironment hostingEnvironment, IIOHelper ioHelper, IContentTypeProvider contentTypeProvider)
33-
: this(GetRequestRootPath(options, hostingEnvironment), new BlobContainerClient(options.ConnectionString, options.ContainerName), ioHelper, contentTypeProvider, options.ContainerRootPath)
33+
: this(GetRequestRootPath(options, hostingEnvironment), options.CreateBlobContainerClient(), ioHelper, contentTypeProvider, options.ContainerRootPath)
3434
{ }
3535

3636
/// <summary>
@@ -75,7 +75,7 @@ public static Response<BlobContainerInfo> CreateIfNotExists(AzureBlobFileSystemO
7575
{
7676
ArgumentNullException.ThrowIfNull(options);
7777

78-
return new BlobContainerClient(options.ConnectionString, options.ContainerName).CreateIfNotExists(accessType);
78+
return options.CreateBlobContainerClient().CreateIfNotExists(accessType);
7979
}
8080

8181
/// <inheritdoc />

src/Umbraco.StorageProviders.AzureBlob/IO/AzureBlobFileSystemOptions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.ComponentModel.DataAnnotations;
2+
using Azure.Storage.Blobs;
23

34
namespace Umbraco.StorageProviders.AzureBlob.IO;
45

@@ -34,4 +35,20 @@ public sealed class AzureBlobFileSystemOptions
3435
/// </summary>
3536
[Required]
3637
public required string VirtualPath { get; set; }
38+
39+
/// <summary>
40+
/// Gets or sets the Azure Blob Container client factory.
41+
/// </summary>
42+
/// <value>
43+
/// The Azure Blob Container client factory.
44+
/// </value>
45+
internal Func<AzureBlobFileSystemOptions, BlobContainerClient> BlobContainerClientFactory { get; set; } = DefaultBlobContainerClientFactory;
46+
47+
/// <summary>
48+
/// Gets the default Azure Blob Container client factory.
49+
/// </summary>
50+
/// <value>
51+
/// The default Azure Blob Container client factory.
52+
/// </value>
53+
internal static Func<AzureBlobFileSystemOptions, BlobContainerClient> DefaultBlobContainerClientFactory => options => new BlobContainerClient(options.ConnectionString, options.ContainerName);
3754
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using Azure.Storage.Blobs;
2+
3+
namespace Umbraco.StorageProviders.AzureBlob.IO;
4+
5+
/// <summary>
6+
/// Extension methods for <see cref="AzureBlobFileSystemOptions" />.
7+
/// </summary>
8+
public static class AzureBlobFileSystemOptionsExtensions
9+
{
10+
/// <summary>
11+
/// Creates a <see cref="BlobContainerClient" /> using the default constructor accepting a connection string and container name.
12+
/// </summary>
13+
/// <param name="options">The Azure Blob File System options.</param>
14+
/// <exception cref="System.ArgumentNullException"><paramref name="options" /> is <c>null</c>.</exception>
15+
public static void CreateBlobContainerClientUsingDefault(this AzureBlobFileSystemOptions options)
16+
{
17+
ArgumentNullException.ThrowIfNull(options);
18+
19+
options.BlobContainerClientFactory = AzureBlobFileSystemOptions.DefaultBlobContainerClientFactory;
20+
}
21+
22+
/// <summary>
23+
/// Creates a <see cref="BlobContainerClient" /> using the default constructor accepting a connection string, container name and Blob client options.
24+
/// </summary>
25+
/// <param name="options">The Azure Blob File System options.</param>
26+
/// <param name="blobClientOptions">The Azure Blob client options.</param>
27+
/// <exception cref="System.ArgumentNullException"><paramref name="options" /> is <c>null</c>.</exception>
28+
public static void CreateBlobContainerClientUsingOptions(this AzureBlobFileSystemOptions options, BlobClientOptions blobClientOptions)
29+
{
30+
ArgumentNullException.ThrowIfNull(options);
31+
32+
options.BlobContainerClientFactory = options => new BlobContainerClient(options.ConnectionString, options.ContainerName, blobClientOptions);
33+
}
34+
35+
/// <summary>
36+
/// Parses the connection string to a URI and invokes the <paramref name="create" /> delegate to create a <see cref="BlobContainerClient" />.
37+
/// </summary>
38+
/// <param name="options">The Azure Blob File System options.</param>
39+
/// <param name="create">The delegate to create a <see cref="BlobContainerClient" /> from the parsed <see cref="Uri" />.</param>
40+
/// <remarks>
41+
/// If the connection string can't be parsed to a URI, the existing delegate will be used instead.
42+
/// </remarks>
43+
/// <exception cref="System.ArgumentNullException"><paramref name="options" /> is <c>null</c>.</exception>
44+
public static void TryCreateBlobContainerClientUsingUri(this AzureBlobFileSystemOptions options, Func<Uri, BlobContainerClient> create)
45+
{
46+
ArgumentNullException.ThrowIfNull(options);
47+
48+
Func<AzureBlobFileSystemOptions, BlobContainerClient> defaultCreate = options.BlobContainerClientFactory;
49+
options.BlobContainerClientFactory = options =>
50+
{
51+
if (Uri.TryCreate(options.ConnectionString, UriKind.Absolute, out Uri? blobContainerUri))
52+
{
53+
var blobUriBuilder = new BlobUriBuilder(blobContainerUri)
54+
{
55+
BlobContainerName = options.ContainerName
56+
};
57+
58+
return create(blobUriBuilder.ToUri());
59+
}
60+
61+
return defaultCreate(options);
62+
};
63+
}
64+
65+
/// <summary>
66+
/// Creates the Azure Blob Container client using the configured factory.
67+
/// </summary>
68+
/// <param name="options">The Azure Blob File System options.</param>
69+
/// <returns>
70+
/// The Azure Blob Container client.
71+
/// </returns>
72+
/// <exception cref="System.ArgumentNullException"><paramref name="options" /> is <c>null</c>.</exception>
73+
public static BlobContainerClient CreateBlobContainerClient(this AzureBlobFileSystemOptions options)
74+
{
75+
ArgumentNullException.ThrowIfNull(options);
76+
77+
return options.BlobContainerClientFactory(options);
78+
}
79+
}

0 commit comments

Comments
 (0)