# Azure Blob

## Description

Azure Blob Storage can be used as the backing implementation for Flex file operations. Your application code should depend on `IFlexFileStore`, while Flex provides the Azure Blob implementation and wiring.

## Important concepts

* **`IFlexFileStore` is the contract**: uploads/downloads are done through a shared abstraction.
* **Streaming**: uploads use a `Stream`; downloads return a `Stream?` (handle `null` if the file doesn’t exist).
* **Container scoping**: the provider typically writes into a configured container.

## Configuration in DI

Add the provider in your DI composition root (commonly in `EndPoints/...CommonConfigs/OtherApplicationServicesConfig.cs`).

```csharp
public static class OtherApplicationServicesConfig
{
	public static IServiceCollection AddOtherApplicationServices(
		this IServiceCollection services,
		IConfiguration configuration)
	{
		// Registers Azure Blob as the IFlexFileStore bridge.
		services.AddFlexAzureBlobFileStore(configuration);
		return services;
	}
}
```

## appsettings.json

Configuration is read from `FlexBase:DataStores:File:AzureBlob`.

```json
{
  "FlexBase": {
	"DataStores": {
	  "File": {
		"AzureBlob": {
		  "ConnectionString": "...",
		  "ContainerName": "your-container",
		  "CreateContainerIfNotExists": true
		}
	  }
	}
  }
}
```

## Examples (template-based)

These examples mirror the generated Query and PostBusHandler templates. You do **not** register these types manually—Flex discovers and wires generated Queries/Handlers automatically.

### Upload file (PostBusHandler)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System.IO;
using System.Threading.Tasks;

namespace {YourApplication}.Handlers.Files;

public partial class UploadFileHandler : IAmFlexCommandHandler<UploadFileCommand>
{
	protected string EventCondition = "";
	protected readonly ILogger<UploadFileHandler> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexFileStore _fileStore;

	public UploadFileHandler(ILogger<UploadFileHandler> logger, IFlexHost flexHost, IFlexFileStore fileStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_fileStore = fileStore;
	}

	public virtual async Task Execute(UploadFileCommand cmd, IFlexServiceBusContext serviceBusContext)
	{
		_logger.LogDebug("Starting Upload for {EntityType}", nameof(FileModel));
		var appContext = cmd.Dto.GetAppContext(); // do not remove this line

		var model = _flexHost.GetDomainModel<FileModel>().Create(cmd);

		var fileId = await _fileStore.UploadAsync(
			cmd.Dto.Stream,
			cmd.Dto.FileName,
			cmd.Dto.ContentType,
			cmd.Dto.Metadata);

		await this.Fire(EventCondition, serviceBusContext);
		_logger.LogDebug("Completed Upload for {EntityType} {FileId}, Event: {EventCondition}", nameof(FileModel), fileId, EventCondition);
	}
}

public class UploadFileCommand : FlexCommandBridge<UploadFileDto> { }
public class UploadFileDto : DtoBridge
{
	public Stream Stream { get; set; }
	public string FileName { get; set; }
	public string ContentType { get; set; }
	public IDictionary<string, string>? Metadata { get; set; }
}
public class FileModel : FlexDomainModelBridge { }
```

### Download file (Query)

```csharp
using Microsoft.Extensions.Logging;
using Sumeru.Flex;
using System.IO;
using System.Threading.Tasks;

namespace {YourApplication}.Queries.Files;

public class DownloadFileQuery : FlexiQueryBridgeAsync<FileDownloadOutput>
{
	protected readonly ILogger<DownloadFileQuery> _logger;
	protected readonly IFlexHost _flexHost;
	protected readonly IFlexFileStore _fileStore;
	protected DownloadFileParams _params;

	public DownloadFileQuery(ILogger<DownloadFileQuery> logger, IFlexHost flexHost, IFlexFileStore fileStore)
	{
		_logger = logger;
		_flexHost = flexHost;
		_fileStore = fileStore;
	}

	public virtual DownloadFileQuery AssignParameters(DownloadFileParams @params)
	{
		_params = @params;
		return this;
	}

	public virtual async Task<FileDownloadOutput?> Fetch()
	{
		var appContext = _params.GetAppContext();
		var stream = await _fileStore.DownloadAsync(_params.FileId);
		if (stream == null) return default;

		return new FileDownloadOutput { FileId = _params.FileId, Stream = stream };
	}
}

public class DownloadFileParams : DtoBridge { public string FileId { get; set; } }
public class FileDownloadOutput : DtoBridge
{
	public string FileId { get; set; }
	public Stream Stream { get; set; }
}
```

## Library-specific considerations

* **Connection string**: store it in a secret manager and inject via configuration.
* **Container creation**: `CreateContainerIfNotExists=true` is convenient for dev; production setups often pre-provision containers.
