Setup Azure package

This commit is contained in:
2024-08-16 20:15:11 +02:00
parent 814c43e53a
commit 1ff3c767f0
15 changed files with 789 additions and 2 deletions
+10 -2
View File
@@ -6,6 +6,7 @@ on:
- master
paths:
- Net.Sdk.Web.Extensions/**
- Net.Sdk.Web.Extensions.Azure/**
- ".github/workflows/cd.yaml"
jobs:
@@ -22,6 +23,7 @@ jobs:
Configuration: Release
Solution_Path: Net.Sdk.Web.Extensions.sln
Source_Project_Path: Net.Sdk.Web.Extensions\Net.Sdk.Web.Extensions.csproj
Azure_Project_path: Net.Sdk.Web.Extensions.Azure\Net.Sdk.Web.Extensions.Azure.csproj
Actions_Allow_Unsecure_Commands: true
steps:
@@ -43,11 +45,17 @@ jobs:
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- name: Build AspNetCore.Extensions.NetStandard project
- name: Build Net.Sdk.Extensions project
run: dotnet build $env:Source_Project_Path -c $env:Configuration
- name: Package AspNetCore.Extensions.NetStandard
- name: Package Net.Sdk.Extensions
run: dotnet pack -c Release -o . $env:Source_Project_Path
- name: Build Net.Sdk.Extensions.Azure project
run: dotnet build $env:Azure_Project_Path -c $env:Configuration
- name: Package Net.Sdk.Extensions.Azure
run: dotnet pack -c Release -o . $env:Azure_Project_Path
- name: Publish
run: dotnet nuget push *.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+2
View File
@@ -6,6 +6,7 @@ on:
- master
paths:
- Net.Sdk.Web.Extensions/**
- Net.Sdk.Web.Extensions.Azure/**
- ".github/workflows/ci.yaml"
jobs:
@@ -21,6 +22,7 @@ jobs:
env:
Solution_Path: Net.Sdk.Web.Extensions.sln
Source_Project_Path: Net.Sdk.Web.Extensions\Net.Sdk.Web.Extensions.csproj
Azure_Project_path: Net.Sdk.Web.Extensions.Azure\Net.Sdk.Web.Extensions.Azure.csproj
Actions_Allow_Unsecure_Commands: true
steps:
@@ -0,0 +1,35 @@
using Azure;
namespace Net.Sdk.Web.Extensions.Azure;
internal sealed class InterceptingAsyncPageable<T> : AsyncPageable<T> where T : notnull
{
private readonly Action<Page<T>> interceptPage;
private readonly Action interceptSuccess;
private readonly AsyncPageable<T> originalPageable;
public InterceptingAsyncPageable(AsyncPageable<T> originalPageable, Action<Page<T>> interceptPage, Action interceptSuccess)
{
this.originalPageable = originalPageable;
this.interceptPage = interceptPage;
this.interceptSuccess = interceptSuccess;
}
public override async IAsyncEnumerable<Page<T>> AsPages(string? continuationToken = null, int? pageSizeHint = null)
{
await foreach (var page in this.originalPageable.AsPages(continuationToken, pageSizeHint))
{
this.interceptPage(page);
yield return page;
}
}
public async override IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
await foreach (var item in this.originalPageable)
{
this.interceptSuccess();
yield return item;
}
}
}
@@ -0,0 +1,35 @@
using Azure;
namespace Net.Sdk.Web.Extensions.Azure;
internal sealed class InterceptingPageable<T> : Pageable<T> where T : notnull
{
private readonly Action<Page<T>> interceptPage;
private readonly Action interceptSuccess;
private readonly Pageable<T> originalPageable;
public InterceptingPageable(Pageable<T> originalPageable, Action<Page<T>> interceptPage, Action interceptSuccess)
{
this.originalPageable = originalPageable;
this.interceptPage = interceptPage;
this.interceptSuccess = interceptSuccess;
}
public override IEnumerable<Page<T>> AsPages(string? continuationToken = null, int? pageSizeHint = null)
{
foreach (var page in this.originalPageable.AsPages(continuationToken, pageSizeHint))
{
this.interceptPage(page);
yield return page;
}
}
public override IEnumerator<T> GetEnumerator()
{
foreach (var item in this.originalPageable)
{
this.interceptSuccess();
yield return item;
}
}
}
@@ -0,0 +1,38 @@
using Azure.Core;
using Azure;
using Azure.Storage.Blobs;
using Azure.Storage;
using Net.Sdk.Web.Extensions.Azure.Options;
namespace Net.Sdk.Web.Extensions.Azure;
public class NamedBlobContainerClient<TCategory> : BlobContainerClient
{
public NamedBlobContainerClient(string connectionString, string blobContainerName) : base(connectionString, blobContainerName)
{
}
public NamedBlobContainerClient(Uri blobContainerUri, BlobClientOptions? options = null) : base(blobContainerUri, options)
{
}
public NamedBlobContainerClient(string connectionString, string blobContainerName, BlobClientOptions options) : base(connectionString, blobContainerName, options)
{
}
public NamedBlobContainerClient(Uri blobContainerUri, StorageSharedKeyCredential credential, BlobClientOptions? options = null) : base(blobContainerUri, credential, options)
{
}
public NamedBlobContainerClient(Uri blobContainerUri, AzureSasCredential credential, BlobClientOptions? options = null) : base(blobContainerUri, credential, options)
{
}
public NamedBlobContainerClient(Uri blobContainerUri, TokenCredential credential, BlobClientOptions? options = null) : base(blobContainerUri, credential, options)
{
}
protected NamedBlobContainerClient()
{
}
}
@@ -0,0 +1,359 @@
using Azure.Core;
using Azure;
using System.Diagnostics;
using System.Extensions;
using System.Linq.Expressions;
using Azure.Data.Tables;
using System.Core.Extensions;
using Azure.Data.Tables.Models;
namespace Net.Sdk.Web.Extensions.Azure;
public class NamedTableClient<TCategory> : TableClient
{
private readonly ILogger<NamedTableClient<TCategory>> logger;
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, Uri endpoint, TableClientOptions? options = null) : base(endpoint, options)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, string connectionString, string tableName) : base(connectionString, tableName)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, Uri endpoint, AzureSasCredential credential, TableClientOptions? options = null) : base(endpoint, credential, options)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, Uri endpoint, string tableName, TableSharedKeyCredential credential) : base(endpoint, tableName, credential)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, string connectionString, string tableName, TableClientOptions? options = null) : base(connectionString, tableName, options)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, Uri endpoint, string tableName, TableSharedKeyCredential credential, TableClientOptions? options = null) : base(endpoint, tableName, credential, options)
{
this.logger = logger.ThrowIfNull();
}
public NamedTableClient(ILogger<NamedTableClient<TCategory>> logger, Uri endpoint, string tableName, TokenCredential tokenCredential, TableClientOptions? options = null) : base(endpoint, tableName, tokenCredential, options)
{
this.logger = logger.ThrowIfNull();
}
public override Response<TableItem> Create(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.Create(cancellationToken), nameof(this.Create));
}
public override Response<TableItem> CreateIfNotExists(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.CreateIfNotExists(cancellationToken), nameof(this.CreateIfNotExists));
}
public override Response AddEntity<T>(T entity, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.AddEntity(entity, cancellationToken), nameof(this.AddEntity));
}
public override Response Delete(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.Delete(cancellationToken), nameof(this.Delete));
}
public override Response DeleteEntity(string partitionKey, string rowKey, ETag ifMatch = default, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.DeleteEntity(partitionKey, rowKey, ifMatch, cancellationToken), nameof(this.DeleteEntity));
}
public override Response UpdateEntity<T>(T entity, ETag ifMatch, TableUpdateMode mode = TableUpdateMode.Merge, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.UpdateEntity(entity, ifMatch, mode, cancellationToken), nameof(this.UpdateEntity));
}
public override Response UpsertEntity<T>(T entity, TableUpdateMode mode = TableUpdateMode.Merge, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.UpsertEntity(entity, mode, cancellationToken), nameof(this.UpsertEntity));
}
public override Response<T> GetEntity<T>(string partitionKey, string rowKey, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.GetEntity<T>(partitionKey, rowKey, select, cancellationToken), nameof(this.GetEntity));
}
public override NullableResponse<T> GetEntityIfExists<T>(string partitionKey, string rowKey, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.GetEntityIfExists<T>(partitionKey, rowKey, select, cancellationToken), nameof(GetEntityIfExists));
}
public override Pageable<T> Query<T>(string? filter = null, int? maxPerPage = null, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.Query<T>(filter, maxPerPage, select, cancellationToken), nameof(this.Query));
}
public override Pageable<T> Query<T>(Expression<Func<T, bool>> filter, int? maxPerPage = null, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.Query(filter, maxPerPage, select, cancellationToken), nameof(this.Query));
}
public override Response<IReadOnlyList<Response>> SubmitTransaction(IEnumerable<TableTransactionAction> transactionActions, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.SubmitTransaction(transactionActions, cancellationToken), nameof(this.SubmitTransaction));
}
public override Task<Response<TableItem>> CreateAsync(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.CreateAsync(cancellationToken), nameof(this.CreateAsync));
}
public override Task<Response<TableItem>> CreateIfNotExistsAsync(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.CreateIfNotExistsAsync(cancellationToken), nameof(this.CreateIfNotExistsAsync));
}
public override Task<Response> AddEntityAsync<T>(T entity, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.AddEntityAsync(entity, cancellationToken), nameof(this.AddEntityAsync));
}
public override Task<Response> DeleteAsync(CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.DeleteAsync(cancellationToken), nameof(this.DeleteAsync));
}
public override Task<Response> DeleteEntityAsync(string partitionKey, string rowKey, ETag ifMatch = default, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.DeleteEntityAsync(partitionKey, rowKey, ifMatch, cancellationToken), nameof(this.DeleteEntityAsync));
}
public override Task<Response> UpdateEntityAsync<T>(T entity, ETag ifMatch, TableUpdateMode mode = TableUpdateMode.Merge, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.UpdateEntityAsync(entity, ifMatch, mode, cancellationToken), nameof(this.UpdateEntityAsync));
}
public override Task<Response> UpsertEntityAsync<T>(T entity, TableUpdateMode mode = TableUpdateMode.Merge, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.UpsertEntityAsync(entity, mode, cancellationToken), nameof(this.UpsertEntityAsync));
}
public override Task<Response<T>> GetEntityAsync<T>(string partitionKey, string rowKey, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.GetEntityAsync<T>(partitionKey, rowKey, select, cancellationToken), nameof(this.GetEntityAsync));
}
public override Task<NullableResponse<T>> GetEntityIfExistsAsync<T>(string partitionKey, string rowKey, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.GetEntityIfExistsAsync<T>(partitionKey, rowKey, select, cancellationToken), nameof(this.GetEntityIfExistsAsync));
}
public override AsyncPageable<T> QueryAsync<T>(Expression<Func<T, bool>> filter, int? maxPerPage = null, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.QueryAsync(filter, maxPerPage, select, cancellationToken), nameof(this.QueryAsync));
}
public override AsyncPageable<T> QueryAsync<T>(string? filter = null, int? maxPerPage = null, IEnumerable<string>? select = null, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.QueryAsync<T>(filter, maxPerPage, select, cancellationToken), nameof(this.QueryAsync));
}
public override Task<Response<IReadOnlyList<Response>>> SubmitTransactionAsync(IEnumerable<TableTransactionAction> transactionActions, CancellationToken cancellationToken = default)
{
return this.LogOperation(() => base.SubmitTransactionAsync(transactionActions, cancellationToken), nameof(this.SubmitTransactionAsync));
}
private Response LogOperation(Func<Response> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = func();
scopedLogger.LogInformation("<< {0} {1}ms", response.Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private Response<T> LogOperation<T>(Func<Response<T>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = func();
scopedLogger.LogInformation("<< {0} {1}ms", response.GetRawResponse().Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private NullableResponse<T> LogOperation<T>(Func<NullableResponse<T>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = func();
scopedLogger.LogInformation("<< {0} {1}ms", response.GetRawResponse().Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private Pageable<T> LogOperation<T>(Func<Pageable<T>> func, string operationName)
where T : notnull
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
var response = func();
return new InterceptingPageable<T>(response, page =>
{
scopedLogger.LogInformation("<< {0} {1}ms", page.GetRawResponse().Status, sw.ElapsedMilliseconds);
}, () =>
{
scopedLogger.LogInformation("<< 200 {1}ms", sw.ElapsedMilliseconds);
});
}
private Response<IReadOnlyList<Response>> LogOperation(Func<Response<IReadOnlyList<Response>>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = func();
foreach (var action in response.Value)
{
scopedLogger.LogInformation("<< {0} {1}ms", action.Status, sw.ElapsedMilliseconds);
}
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private async Task<Response> LogOperation(Func<Task<Response>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = await func();
scopedLogger.LogInformation("<< {0} {1}ms", response.Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private async Task<Response<T>> LogOperation<T>(Func<Task<Response<T>>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = await func();
scopedLogger.LogInformation("<< {0} {1}ms", response.GetRawResponse().Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private async Task<NullableResponse<T>> LogOperation<T>(Func<Task<NullableResponse<T>>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = await func();
scopedLogger.LogInformation("<< {0} {1}ms", response.GetRawResponse().Status, sw.ElapsedMilliseconds);
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
private AsyncPageable<T> LogOperation<T>(Func<AsyncPageable<T>> func, string operationName)
where T : notnull
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
var response = func();
return new InterceptingAsyncPageable<T>(response, page =>
{
scopedLogger.LogInformation("<< {0} {1}ms", page.GetRawResponse().Status, sw.ElapsedMilliseconds);
}, () =>
{
scopedLogger.LogInformation("<< 200 {1}ms", sw.ElapsedMilliseconds);
});
}
private async Task<Response<IReadOnlyList<Response>>> LogOperation(Func<Task<Response<IReadOnlyList<Response>>>> func, string operationName)
{
var scopedLogger = this.logger.CreateScopedLogger(operationName, string.Empty);
var sw = Stopwatch.StartNew();
scopedLogger.LogInformation(">> {0}", this.Uri);
try
{
var response = await func();
foreach (var action in response.Value)
{
scopedLogger.LogInformation("<< {0} {1}ms", action.Status, sw.ElapsedMilliseconds);
}
return response;
}
catch (RequestFailedException ex)
{
scopedLogger.LogInformation("<< {0} {1}ms", ex.Status, sw.ElapsedMilliseconds);
throw;
}
}
}
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Library</OutputType>
<IsPackable>true</IsPackable>
<Version>0.7</Version>
<Authors>Alexandru Macocian</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RepositoryUrl>https://github.com/AlexMacocian/Net.Sdk.Web.Extensions</RepositoryUrl>
<Description>Azure specific extensions for Microsoft.NET.Sdk.Web</Description>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE" Link="LICENSE">
<PackagePath></PackagePath>
<Pack>True</Pack>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.Core" Version="1.42.0" />
<PackageReference Include="Azure.Data.Tables" Version="12.9.0" />
<PackageReference Include="Azure.Identity" Version="1.12.0" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.21.2" />
<PackageReference Include="Net.Sdk.Web.Extensions" Version="0.7.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;
namespace Net.Sdk.Web.Extensions.Azure.Options;
public sealed class AzureCredentialsOptions : IAzureClientSecretCredentialOptions
{
[JsonPropertyName(nameof(ClientSecret))]
public string ClientSecret { get; set; } = default!;
[JsonPropertyName(nameof(ClientId))]
public string ClientId { get; set; } = default!;
[JsonPropertyName(nameof(TenantId))]
public string TenantId { get; set; } = default!;
}
@@ -0,0 +1,6 @@
namespace Net.Sdk.Web.Extensions.Azure.Options;
public interface IAzureBlobStorageOptions
{
string ContainerName { get; set; }
}
@@ -0,0 +1,6 @@
namespace Net.Sdk.Web.Extensions.Azure.Options;
public interface IAzureClientSecretCredentialOptions : IAzureCredentialOptions
{
string ClientSecret { get; set; }
}
@@ -0,0 +1,8 @@
namespace Net.Sdk.Web.Extensions.Azure.Options;
public interface IAzureCredentialOptions
{
string ClientId { get; set; }
string TenantId { get; set; }
}
@@ -0,0 +1,6 @@
namespace Net.Sdk.Web.Extensions.Azure.Options;
public interface IAzureTableStorageOptions
{
string TableName { get; set; }
}
@@ -0,0 +1,6 @@
namespace Net.Sdk.Web.Extensions.Azure.Options;
public interface IStorageAccountOptions
{
string AccountName { get; }
}
@@ -0,0 +1,224 @@
using Azure.Core;
using Azure.Identity;
using Microsoft.Extensions.Options;
using Net.Sdk.Web.Extensions.Azure.Options;
using System.Core.Extensions;
using System.Extensions;
namespace Net.Sdk.Web.Extensions.Azure;
public static class WebApplicationBuilderExtensions
{
public static WebApplicationBuilder ConfigureAzureClientSecretCredentials(this WebApplicationBuilder builder)
{
builder.ThrowIfNull()
.ConfigureExtended<AzureCredentialsOptions>()
.Services.AddSingleton<TokenCredential>(sp =>
{
var options = sp.GetRequiredService<IOptions<AzureCredentialsOptions>>().Value;
return new ClientSecretCredential(options.TenantId, options.ClientId, options.ClientSecret);
});
return builder;
}
public static WebApplicationBuilder ConfigureAzureClientSecretCredentials<TOptions>(this WebApplicationBuilder builder)
where TOptions : class, IAzureClientSecretCredentialOptions, new()
{
builder.ThrowIfNull()
.ConfigureExtended<TOptions>()
.Services.AddSingleton<TokenCredential>(sp =>
{
var options = sp.GetRequiredService<IOptions<TOptions>>().Value;
return new ClientSecretCredential(options.TenantId, options.ClientId, options.ClientSecret);
});
return builder;
}
public static WebApplicationBuilder WithTableClientSingleton<TCategory, TAccountOptions, TTableOptions>(this WebApplicationBuilder builder)
where TTableOptions : class, IAzureTableStorageOptions, new()
where TAccountOptions : class, IStorageAccountOptions, new()
{
builder.Services.ThrowIfNull()
.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var clientOptions = sp.GetRequiredService<IOptions<TTableOptions>>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{storageOptions.Value.AccountName}.table.core.windows.net"), clientOptions.Value.TableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithTableClientSingleton<TCategory, TAccountOptions>(this WebApplicationBuilder builder, string tableName)
where TAccountOptions : class, IStorageAccountOptions, new()
{
tableName.ThrowIfNull();
builder.Services.ThrowIfNull()
.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{storageOptions.Value.AccountName}.table.core.windows.net"), tableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithTableClientSingleton<TCategory>(this WebApplicationBuilder builder, string accountName, string tableName)
{
tableName.ThrowIfNull();
accountName.ThrowIfNull();
builder.Services.ThrowIfNull()
.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{accountName}.table.core.windows.net"), tableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithTableClientScoped<TCategory, TAccountOptions, TTableOptions>(this WebApplicationBuilder builder)
where TTableOptions : class, IAzureTableStorageOptions, new()
where TAccountOptions : class, IStorageAccountOptions, new()
{
builder.Services.ThrowIfNull()
.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var clientOptions = sp.GetRequiredService<IOptions<TTableOptions>>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{storageOptions.Value.AccountName}.table.core.windows.net"), clientOptions.Value.TableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithTableClientScoped<TCategory, TAccountOptions>(this WebApplicationBuilder builder, string tableName)
where TAccountOptions : class, IStorageAccountOptions, new()
{
tableName.ThrowIfNull();
builder.Services.ThrowIfNull()
.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{storageOptions.Value.AccountName}.table.core.windows.net"), tableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithTableClientScoped<TCategory>(this WebApplicationBuilder builder, string accountName, string tableName)
{
tableName.ThrowIfNull();
accountName.ThrowIfNull();
builder.Services.ThrowIfNull()
.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var logger = sp.GetRequiredService<ILogger<NamedTableClient<TCategory>>>();
return new NamedTableClient<TCategory>(logger, new Uri($"https://{accountName}.table.core.windows.net"), tableName, tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientSingleton<TCategory, TAccountOptions, TBlobContainerOptions>(this WebApplicationBuilder builder)
where TBlobContainerOptions : class, IAzureBlobStorageOptions, new()
where TAccountOptions : class, IStorageAccountOptions, new()
{
builder.ThrowIfNull()
.Services.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var clientOptions = sp.GetRequiredService<IOptions<TBlobContainerOptions>>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{storageOptions.Value.AccountName}.blob.core.windows.net/{clientOptions.Value.ContainerName}"), tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientSingleton<TCategory, TAccountOptions>(this WebApplicationBuilder builder, string containerName)
where TAccountOptions : class, IStorageAccountOptions, new()
{
containerName.ThrowIfNull();
builder.ThrowIfNull()
.Services.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{storageOptions.Value.AccountName}.blob.core.windows.net/{containerName}"), tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientSingleton<TCategory>(this WebApplicationBuilder builder, string accountName, string containerName)
{
accountName.ThrowIfNull();
containerName.ThrowIfNull();
builder.ThrowIfNull()
.Services.AddSingleton(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{accountName}.blob.core.windows.net/{containerName}"), tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientScoped<TCategory, TAccountOptions, TBlobContainerOptions>(this WebApplicationBuilder builder)
where TBlobContainerOptions : class, IAzureBlobStorageOptions, new()
where TAccountOptions : class, IStorageAccountOptions, new()
{
builder.ThrowIfNull()
.Services.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
var clientOptions = sp.GetRequiredService<IOptions<TBlobContainerOptions>>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{storageOptions.Value.AccountName}.blob.core.windows.net/{clientOptions.Value.ContainerName}"), tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientScoped<TCategory, TAccountOptions>(this WebApplicationBuilder builder, string containerName)
where TAccountOptions : class, IStorageAccountOptions, new()
{
containerName.ThrowIfNull();
builder.ThrowIfNull()
.Services.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
var storageOptions = sp.GetRequiredService<IOptions<TAccountOptions>>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{storageOptions.Value.AccountName}.blob.core.windows.net/{containerName}"), tokenCredential, default);
});
return builder;
}
public static WebApplicationBuilder WithBlobContainerClientScoped<TCategory>(this WebApplicationBuilder builder, string accountName, string containerName)
{
accountName.ThrowIfNull();
containerName.ThrowIfNull();
builder.ThrowIfNull()
.Services.AddScoped(sp =>
{
var tokenCredential = sp.GetRequiredService<TokenCredential>();
return new NamedBlobContainerClient<TCategory>(new Uri($"https://{accountName}.blob.core.windows.net/{containerName}"), tokenCredential, default);
});
return builder;
}
}
+6
View File
@@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
.github\workflows\ci.yaml = .github\workflows\ci.yaml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net.Sdk.Web.Extensions.Azure", "Net.Sdk.Web.Extensions.Azure\Net.Sdk.Web.Extensions.Azure.csproj", "{F9D782AF-2E99-497A-B5C2-5DF6FB254EE5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{D0570CEC-F740-4332-AC10-8491974D1827}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0570CEC-F740-4332-AC10-8491974D1827}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0570CEC-F740-4332-AC10-8491974D1827}.Release|Any CPU.Build.0 = Release|Any CPU
{F9D782AF-2E99-497A-B5C2-5DF6FB254EE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F9D782AF-2E99-497A-B5C2-5DF6FB254EE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F9D782AF-2E99-497A-B5C2-5DF6FB254EE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F9D782AF-2E99-497A-B5C2-5DF6FB254EE5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE