Skip to content

Commit b52c7a4

Browse files
Merge pull request #658 from johelvisguzman/GH-657
(GH-657) Fix container name building within the azure storage blob context
2 parents 44cc360 + bc2c0c4 commit b52c7a4

File tree

16 files changed

+346
-52
lines changed

16 files changed

+346
-52
lines changed

benchmarks/DotNetToolkit.Repository.Performance/Benchmarks/BenchmarkBase.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ protected IRepositoryOptions BuildOptions(ContextProviderType provider)
6363
{
6464
builder.UseAzureStorageBlob(
6565
connectionString: "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;",
66-
container: Guid.NewGuid().ToString(),
6766
createIfNotExists: true);
6867
break;
6968
}

benchmarks/DotNetToolkit.Repository.Performance/Program.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,23 @@ static void Main(string[] args)
1515
EnsureDBSetup();
1616
Console.WriteLine("// * Database Setup: End *");
1717

18+
#if NETCORE
19+
Console.WriteLine("// * AzureStorageEmulator: Clear All *");
20+
Running.AzureStorageEmulatorManager.Clear();
21+
1822
Console.WriteLine("// * AzureStorageEmulator: Start *");
19-
Running.AzureStorageEmulatorManager.Start();
23+
Running.AzureStorageEmulatorManager.Start();
24+
#endif
2025

2126
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new Config());
2227

28+
#if NETCORE
2329
Running.AzureStorageEmulatorManager.Stop();
2430
Console.WriteLine("// * AzureStorageEmulator: End *");
31+
32+
Console.WriteLine("// * AzureStorageEmulator: Clear All *");
33+
Running.AzureStorageEmulatorManager.Clear();
34+
#endif
2535
}
2636

2737
private static void EnsureDBSetup()

benchmarks/DotNetToolkit.Repository.Performance/Running/AzureStorageEmulatorManager.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ static Process GetProcess()
2525
WindowStyle = ProcessWindowStyle.Hidden
2626
};
2727

28+
static readonly ProcessStartInfo _clearStorageEmulator = new ProcessStartInfo
29+
{
30+
FileName = _azureStorageEmulatorPath,
31+
Arguments = "clear all",
32+
UseShellExecute = false,
33+
WindowStyle = ProcessWindowStyle.Hidden
34+
};
35+
2836
static readonly ProcessStartInfo _stopStorageEmulator = new ProcessStartInfo
2937
{
3038
FileName = _azureStorageEmulatorPath,
@@ -49,6 +57,14 @@ public static void Start()
4957
}
5058
}
5159

60+
public static void Clear()
61+
{
62+
using (var process = Process.Start(_clearStorageEmulator))
63+
{
64+
process.WaitForExit();
65+
}
66+
}
67+
5268
public static void Stop()
5369
{
5470
using (var process = Process.Start(_stopStorageEmulator))
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace DotNetToolkit.Repository.AzureStorageBlob
2+
{
3+
/// <summary>
4+
/// Builds a container name for the azure storage blob for the specified type.
5+
/// </summary>
6+
public interface IAzureStorageBlobContainerNameBuilder
7+
{
8+
/// <summary>
9+
/// Returns a custom container name for the specified type.
10+
/// </summary>
11+
string Build<T>();
12+
}
13+
}

src/DotNetToolkit.Repository.AzureStorageBlob/IAzureStorageBlobRepositoryContext.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
public interface IAzureStorageBlobRepositoryContext : IRepositoryContextAsync
1111
{
1212
/// <summary>
13-
/// Gest the cloud blob container.
13+
/// Gest the cloud blob client.
1414
/// </summary>
15-
BlobContainerClient BlobContainer { get; }
15+
BlobServiceClient Client { get; }
1616
}
1717
}

src/DotNetToolkit.Repository.AzureStorageBlob/Internal/AzureStorageBlobRepositoryContext.cs

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,40 @@
1818

1919
internal class AzureStorageBlobRepositoryContext : LinqEnumerableRepositoryContextBase, IAzureStorageBlobRepositoryContext
2020
{
21+
#region Fields
22+
23+
private readonly IAzureStorageBlobContainerNameBuilder _containerNameBuilder;
24+
private readonly bool _createContainerIfNotExists;
25+
26+
#endregion
27+
2128
#region Constructors
2229

23-
public AzureStorageBlobRepositoryContext(string connectionString, string container = null, bool createIfNotExists = false)
30+
public AzureStorageBlobRepositoryContext(string connectionString, IAzureStorageBlobContainerNameBuilder containerNameBuilder = null, bool createIfNotExists = false)
2431
{
2532
Guard.NotEmpty(connectionString, nameof(connectionString));
2633

27-
var client = new BlobServiceClient(connectionString);
28-
29-
if (string.IsNullOrEmpty(container))
30-
container = GetType().Name.ToLower();
34+
_containerNameBuilder = containerNameBuilder ?? new DefaultContainerNameBuilder();
35+
_createContainerIfNotExists = createIfNotExists;
3136

32-
BlobContainer = client.GetBlobContainerClient(container);
33-
34-
if (createIfNotExists)
35-
BlobContainer.CreateIfNotExists();
37+
Client = new BlobServiceClient(connectionString);
3638
}
3739

3840
#endregion
3941

4042
#region Private Methods
4143

44+
private BlobContainerClient GetBlobContainer<TEntity>()
45+
{
46+
var container = _containerNameBuilder.Build<TEntity>();
47+
var blobContainer = Client.GetBlobContainerClient(container);
48+
49+
if (_createContainerIfNotExists)
50+
blobContainer.CreateIfNotExists();
51+
52+
return blobContainer;
53+
}
54+
4255
private IAsyncEnumerable<TEntity> AsAsyncEnumerable<TEntity>() where TEntity : class
4356
{
4457
return DownloadEntitiesAsync<TEntity>();
@@ -48,15 +61,16 @@ private BlobClient GetBlobClient<TEntity>(TEntity entity) where TEntity : class
4861
{
4962
var keyValues = Conventions.GetPrimaryKeyValues(entity);
5063
var key = string.Join(":", keyValues);
64+
var blobContainer = GetBlobContainer<TEntity>();
5165

52-
return BlobContainer.GetBlobClient(key);
66+
return blobContainer.GetBlobClient(key);
5367
}
5468

55-
private TEntity DownloadEntity<TEntity>(string blobName) where TEntity : class
69+
private TEntity DownloadEntity<TEntity>(BlobContainerClient blobContainer, string blobName) where TEntity : class
5670
{
5771
try
5872
{
59-
var blob = BlobContainer.GetBlobClient(blobName);
73+
var blob = blobContainer.GetBlobClient(blobName);
6074
var contentResult = blob.DownloadContent();
6175
var result = contentResult.Value.Content.ToObjectFromJson<TEntity>();
6276

@@ -68,12 +82,41 @@ private TEntity DownloadEntity<TEntity>(string blobName) where TEntity : class
6882
}
6983
}
7084

71-
private async Task<TEntity> DownloadEntityAsync<TEntity>(string blobName, CancellationToken cancellationToken = default) where TEntity : class
85+
private IEnumerable<TEntity> DownloadEntities<TEntity>() where TEntity : class
86+
{
87+
var blobContainer = GetBlobContainer<TEntity>();
88+
foreach (var blobItem in blobContainer.GetBlobs())
89+
{
90+
yield return DownloadEntity<TEntity>(blobContainer, blobItem.Name);
91+
}
92+
}
93+
94+
private async Task<BlobContainerClient> GetBlobContainerAsync<TEntity>()
95+
{
96+
var container = _containerNameBuilder.Build<TEntity>();
97+
var blobContainer = Client.GetBlobContainerClient(container);
98+
99+
if (_createContainerIfNotExists)
100+
await blobContainer.CreateIfNotExistsAsync();
101+
102+
return blobContainer;
103+
}
104+
105+
private async Task<BlobClient> GetBlobClientAsync<TEntity>(TEntity entity) where TEntity : class
106+
{
107+
var keyValues = Conventions.GetPrimaryKeyValues(entity);
108+
var key = string.Join(":", keyValues);
109+
var blobContainer = await GetBlobContainerAsync<TEntity>();
110+
111+
return blobContainer.GetBlobClient(key);
112+
}
113+
114+
private async Task<TEntity> DownloadEntityAsync<TEntity>(BlobContainerClient blobContainer, string blobName, CancellationToken cancellationToken = default) where TEntity : class
72115
{
73116
try
74117
{
75-
var blob = BlobContainer.GetBlobClient(blobName);
76-
var contentResult = await blob.DownloadContentAsync(cancellationToken: cancellationToken);
118+
var blob = blobContainer.GetBlobClient(blobName);
119+
var contentResult = await blob.DownloadContentAsync(cancellationToken);
77120
var result = contentResult.Value.Content.ToObjectFromJson<TEntity>();
78121

79122
return result;
@@ -84,19 +127,12 @@ private async Task<TEntity> DownloadEntityAsync<TEntity>(string blobName, Cancel
84127
}
85128
}
86129

87-
private IEnumerable<TEntity> DownloadEntities<TEntity>() where TEntity : class
88-
{
89-
foreach (var blobItem in BlobContainer.GetBlobs())
90-
{
91-
yield return DownloadEntity<TEntity>(blobItem.Name);
92-
}
93-
}
94-
95130
private async IAsyncEnumerable<TEntity> DownloadEntitiesAsync<TEntity>() where TEntity : class
96131
{
97-
await foreach (var blobItem in BlobContainer.GetBlobsAsync())
132+
var blobContainer = await GetBlobContainerAsync<TEntity>();
133+
await foreach (var blobItem in blobContainer.GetBlobsAsync())
98134
{
99-
yield return DownloadEntity<TEntity>(blobItem.Name);
135+
yield return await DownloadEntityAsync<TEntity>(blobContainer, blobItem.Name);
100136
}
101137
}
102138

@@ -108,12 +144,12 @@ private void UploadEntity<TEntity>(TEntity entity) where TEntity : class
108144
blob.Upload(binaryData, overwrite: true);
109145
}
110146

111-
private Task UploadEntityAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) where TEntity : class
147+
private async Task UploadEntityAsync<TEntity>(TEntity entity, CancellationToken cancellationToken = new CancellationToken()) where TEntity : class
112148
{
113-
var blob = GetBlobClient(entity);
149+
var blob = await GetBlobClientAsync(entity);
114150
var binaryData = BinaryData.FromObjectAsJson<TEntity>(entity);
115151

116-
return blob.UploadAsync(binaryData, overwrite: true, cancellationToken);
152+
await blob.UploadAsync(binaryData, overwrite: true, cancellationToken);
117153
}
118154

119155
#endregion
@@ -129,7 +165,7 @@ protected override IEnumerable<TEntity> AsEnumerable<TEntity>(IFetchQueryStrateg
129165

130166
#region Implementation of IAzureStorageBlobRepositoryContext
131167

132-
public BlobContainerClient BlobContainer { get; }
168+
public BlobServiceClient Client { get; }
133169

134170
#endregion
135171

@@ -161,7 +197,8 @@ public override TEntity Find<TEntity>(IFetchQueryStrategy<TEntity> fetchStrategy
161197
Guard.NotEmpty(keyValues, nameof(keyValues));
162198

163199
var key = string.Join(":", keyValues);
164-
var result = DownloadEntity<TEntity>(key);
200+
var blobContainer = GetBlobContainer<TEntity>();
201+
var result = DownloadEntity<TEntity>(blobContainer, key);
165202

166203
return result;
167204
}
@@ -201,12 +238,13 @@ public Task<int> ExecuteSqlCommandAsync(string sql, CommandType cmdType, Diction
201238

202239
public Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) => Task.FromResult(-1);
203240

204-
public Task<TEntity> FindAsync<TEntity>(CancellationToken cancellationToken, IFetchQueryStrategy<TEntity> fetchStrategy, params object[] keyValues) where TEntity : class
241+
public async Task<TEntity> FindAsync<TEntity>(CancellationToken cancellationToken, IFetchQueryStrategy<TEntity> fetchStrategy, params object[] keyValues) where TEntity : class
205242
{
206243
Guard.NotEmpty(keyValues, nameof(keyValues));
207244

208245
var key = string.Join(":", keyValues);
209-
var result = DownloadEntityAsync<TEntity>(key, cancellationToken);
246+
var blobContainer = await GetBlobContainerAsync<TEntity>();
247+
var result = await DownloadEntityAsync<TEntity>(blobContainer, key, cancellationToken);
210248

211249
return result;
212250
}
@@ -233,7 +271,7 @@ public async Task<PagedQueryResult<IEnumerable<TResult>>> FindAllAsync<TEntity,
233271

234272
var selectorFunc = selector.Compile();
235273

236-
var query = DownloadEntitiesAsync<TEntity>()
274+
var query = AsAsyncEnumerable<TEntity>()
237275
.ApplySpecificationOptions(options)
238276
.ApplySortingOptions(Conventions, options);
239277

@@ -279,7 +317,7 @@ public async Task<PagedQueryResult<Dictionary<TDictionaryKey, TElement>>> ToDict
279317
var keySelectFunc = keySelector.Compile();
280318
var elementSelectorFunc = elementSelector.Compile();
281319

282-
var query = DownloadEntitiesAsync<TEntity>()
320+
var query = AsAsyncEnumerable<TEntity>()
283321
.ApplySpecificationOptions(options)
284322
.ApplySortingOptions(Conventions, options);
285323

src/DotNetToolkit.Repository.AzureStorageBlob/Internal/AzureStorageBlobRepositoryContextFactory.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal class AzureStorageBlobRepositoryContextFactory : IRepositoryContextFact
1212
#region Fields
1313

1414
private readonly string _nameOrConnectionString;
15-
private readonly string _container;
15+
private readonly IAzureStorageBlobContainerNameBuilder _containerNameBuilder;
1616
private readonly bool _createIfNotExists;
1717

1818
#endregion
@@ -23,24 +23,24 @@ internal class AzureStorageBlobRepositoryContextFactory : IRepositoryContextFact
2323
/// Initializes a new instance of the <see cref="AzureStorageBlobRepositoryContext" /> class.
2424
/// </summary>
2525
/// <param name="nameOrConnectionString">Either the database name or a connection string.</param>
26-
/// <param name="container">The name of the container.</param>
26+
/// <param name="containerNameBuilder">The name of the container builder.</param>
2727
/// <param name="createIfNotExists">Creates the container if it does not exist.</param>
28-
public AzureStorageBlobRepositoryContextFactory(string nameOrConnectionString, string container, bool createIfNotExists)
28+
public AzureStorageBlobRepositoryContextFactory(string nameOrConnectionString, IAzureStorageBlobContainerNameBuilder containerNameBuilder, bool createIfNotExists)
2929
{
3030
_nameOrConnectionString = Guard.NotEmpty(nameOrConnectionString, nameof(nameOrConnectionString));
31-
_container = Guard.NotEmpty(container, nameof(container));
31+
_containerNameBuilder = Guard.NotNull(containerNameBuilder, nameof(containerNameBuilder));
3232
_createIfNotExists = createIfNotExists;
3333
}
3434

3535
/// <summary>
3636
/// Initializes a new instance of the <see cref="AzureStorageBlobRepositoryContext" /> class.
3737
/// </summary>
3838
/// <param name="nameOrConnectionString">Either the database name or a connection string.</param>
39-
/// <param name="container">The name of the container.</param>
40-
public AzureStorageBlobRepositoryContextFactory(string nameOrConnectionString, string container)
39+
/// <param name="containerNameBuilder">The name of the container builder.</param>
40+
public AzureStorageBlobRepositoryContextFactory(string nameOrConnectionString, IAzureStorageBlobContainerNameBuilder containerNameBuilder)
4141
{
4242
_nameOrConnectionString = Guard.NotEmpty(nameOrConnectionString, nameof(nameOrConnectionString));
43-
_container = Guard.NotEmpty(container, nameof(container));
43+
_containerNameBuilder = Guard.NotNull(containerNameBuilder, nameof(containerNameBuilder));
4444
}
4545

4646
/// <summary>
@@ -73,7 +73,7 @@ public AzureStorageBlobRepositoryContextFactory(string nameOrConnectionString)
7373
/// <returns>The new repository context.</returns>
7474
public IRepositoryContext Create()
7575
{
76-
return new AzureStorageBlobRepositoryContext(_nameOrConnectionString, _container, _createIfNotExists);
76+
return new AzureStorageBlobRepositoryContext(_nameOrConnectionString, _containerNameBuilder, _createIfNotExists);
7777
}
7878

7979
#endregion
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DotNetToolkit.Repository.AzureStorageBlob.Internal
2+
{
3+
using Configuration.Conventions.Internal;
4+
5+
internal class DefaultContainerNameBuilder : IAzureStorageBlobContainerNameBuilder
6+
{
7+
public string Build<T>()
8+
{
9+
return ModelConventionHelper.GetTableName<T>().ToLower();
10+
}
11+
}
12+
}

src/DotNetToolkit.Repository.AzureStorageBlob/RepositoryOptionsBuilderExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ public static RepositoryOptionsBuilder UseAzureStorageBlob([NotNull] this Reposi
3232
/// </summary>
3333
/// <param name="source">The repository options builder.</param>
3434
/// <param name="connectionString">The connection string.</param>
35-
/// <param name="container">The name of the container.</param>
35+
/// <param name="containerNameBuilder">The name of the container builder.</param>
3636
/// <param name="createIfNotExists">Creates the container if it does not exist.</param>
3737
/// <returns>The same builder instance.</returns>
38-
public static RepositoryOptionsBuilder UseAzureStorageBlob([NotNull] this RepositoryOptionsBuilder source, [NotNull] string connectionString, string container, bool createIfNotExists = false)
38+
public static RepositoryOptionsBuilder UseAzureStorageBlob([NotNull] this RepositoryOptionsBuilder source, [NotNull] string connectionString, IAzureStorageBlobContainerNameBuilder containerNameBuilder, bool createIfNotExists = false)
3939
{
4040
Guard.NotNull(source, nameof(source));
4141
Guard.NotEmpty(connectionString, nameof(connectionString));
42-
Guard.NotEmpty(container, nameof(container));
42+
Guard.NotNull(containerNameBuilder, nameof(containerNameBuilder));
4343

44-
source.UseInternalContextFactory(new AzureStorageBlobRepositoryContextFactory(connectionString, container, createIfNotExists));
44+
source.UseInternalContextFactory(new AzureStorageBlobRepositoryContextFactory(connectionString, containerNameBuilder, createIfNotExists));
4545

4646
return source;
4747
}

0 commit comments

Comments
 (0)