diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index 585309d8eb..c55f6845a8 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -603,6 +603,10 @@ { "text": "Storage Providers", "items": [ + { + "text": "Memory Provider", + "path": "framework/infrastructure/blob-storing/memory.md" + }, { "text": "File System Provider", "path": "framework/infrastructure/blob-storing/file-system.md" diff --git a/docs/en/framework/infrastructure/blob-storing/memory.md b/docs/en/framework/infrastructure/blob-storing/memory.md new file mode 100644 index 0000000000..0636ad3fb6 --- /dev/null +++ b/docs/en/framework/infrastructure/blob-storing/memory.md @@ -0,0 +1,35 @@ +# BLOB Storing Memory Provider + +Memory Storage Provider is used to store BLOBs in the memory. This is mainly used for unit testing purposes. + +> Read the [BLOB Storing document](../blob-storing) to understand how to use the BLOB storing system. This document only covers how to configure containers to use the memory. + +## Installation + +Use the ABP CLI to add [Volo.Abp.BlobStoring.Memory](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Memory) NuGet package to your project: + +* Install the [ABP CLI](../../../cli) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.Memory` package. +* Run `abp add-package Volo.Abp.BlobStoring.Memory` command. + +If you want to do it manually, install the [Volo.Abp.BlobStoring.Memory](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Memory) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringMemoryModule))]` to the [ABP module](../../architecture/modularity/basics.md) class inside your project. + +## Configuration + +Configuration is done in the `ConfigureServices` method of your [module](../../architecture/modularity/basics.md) class, as explained in the [BLOB Storing document](../blob-storing). + +**Example: Configure to use the Memory storage provider by default** + +````csharp +Configure(options => +{ + options.Containers.ConfigureDefault(container => + { + container.UseMemory(); + }); +}); +```` + +`UseMemory` extension method is used to set the Memory Provider for a container. + +> See the [BLOB Storing document](../blob-storing) to learn how to configure this provider for a specific container. diff --git a/framework/Volo.Abp.slnx b/framework/Volo.Abp.slnx index 286d71fe62..558876b275 100644 --- a/framework/Volo.Abp.slnx +++ b/framework/Volo.Abp.slnx @@ -163,6 +163,7 @@ + @@ -247,5 +248,6 @@ + - + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xml new file mode 100644 index 0000000000..1715698ccd --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xsd new file mode 100644 index 0000000000..ffa6fc4b78 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/Volo.Abp.BlobStoring.Memory.csproj b/framework/src/Volo.Abp.BlobStoring.Memory/Volo.Abp.BlobStoring.Memory.csproj new file mode 100644 index 0000000000..02abcd3836 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/Volo.Abp.BlobStoring.Memory.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0 + enable + Nullable + Volo.Abp.BlobStoring.Memory + Volo.Abp.BlobStoring.Memory + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryModule.cs b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryModule.cs new file mode 100644 index 0000000000..051ecda0e3 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Memory; + +[DependsOn(typeof(AbpBlobStoringModule))] +public class AbpBlobStoringMemoryModule : AbpModule +{ + +} diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..b366039d69 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobContainerConfigurationExtensions.cs @@ -0,0 +1,10 @@ +namespace Volo.Abp.BlobStoring.Memory; + +public static class MemoryBlobContainerConfigurationExtensions +{ + public static BlobContainerConfiguration UseMemory(this BlobContainerConfiguration containerConfiguration) + { + containerConfiguration.ProviderType = typeof(MemoryBlobProvider); + return containerConfiguration; + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobProvider.cs new file mode 100644 index 0000000000..0c499b89cc --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Memory/Volo/Abp/BlobStoring/Memory/MemoryBlobProvider.cs @@ -0,0 +1,65 @@ +using System.Collections.Concurrent; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Memory; + +public class MemoryBlobProvider : BlobProviderBase, ITransientDependency +{ + protected ConcurrentDictionary MemoryStore { get; } + + protected ICurrentTenant CurrentTenant { get; } + + public MemoryBlobProvider(ICurrentTenant currentTenant) + { + MemoryStore = new ConcurrentDictionary(); + + CurrentTenant = currentTenant; + } + + public override async Task SaveAsync(BlobProviderSaveArgs args) + { + var cacheKey = GetCacheKey(args); + + using var buffer = new MemoryStream(); + await args.BlobStream.CopyToAsync(buffer); + var bytes = buffer.ToArray(); + + if (!args.OverrideExisting) + { + if (!MemoryStore.TryAdd(cacheKey, bytes)) + { + throw new BlobAlreadyExistsException( + $"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + } + else + { + MemoryStore.AddOrUpdate(cacheKey, bytes, (_, __) => bytes); + } + } + + public override Task DeleteAsync(BlobProviderDeleteArgs args) + { + return Task.FromResult(MemoryStore.TryRemove(GetCacheKey(args), out _)); + } + + public override Task ExistsAsync(BlobProviderExistsArgs args) + { + return Task.FromResult(MemoryStore.ContainsKey(GetCacheKey(args))); + } + + public override Task GetOrNullAsync(BlobProviderGetArgs args) + { + return MemoryStore.TryGetValue(GetCacheKey(args), out var bytes) + ? Task.FromResult(new MemoryStream(bytes, writable: false)) + : Task.FromResult(null); + } + + protected virtual string GetCacheKey(BlobProviderArgs args) + { + return $"{CurrentTenant.Id}_{args.BlobName}_{args.ContainerName}"; + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo.Abp.BlobStoring.Memory.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo.Abp.BlobStoring.Memory.Tests.csproj new file mode 100644 index 0000000000..9950ceabba --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo.Abp.BlobStoring.Memory.Tests.csproj @@ -0,0 +1,18 @@ + + + + + + net10.0 + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryTestModule.cs new file mode 100644 index 0000000000..d7361df8ec --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/AbpBlobStoringMemoryTestModule.cs @@ -0,0 +1,21 @@ +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Memory; + +[DependsOn( + typeof(AbpBlobStoringMemoryModule), + typeof(AbpBlobStoringTestModule) +)] +public class AbpBlobStoringMemoryTestModule : AbpModule +{ + public override void PostConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseMemory(); + }); + }); + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/MemoryBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/MemoryBlobContainer_Tests.cs new file mode 100644 index 0000000000..3f02dc38f3 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Memory.Tests/Volo/Abp/BlobStoring/Memory/MemoryBlobContainer_Tests.cs @@ -0,0 +1,6 @@ +namespace Volo.Abp.BlobStoring.Memory; + +public class MemoryBlobContainer_Tests : BlobContainer_Tests +{ + +} diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj index 87086825a6..8dcbe959e4 100644 --- a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj @@ -11,6 +11,7 @@ + diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs index e85bac8ce3..a0d3e9d818 100644 --- a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsAdminApplicationTestModule.cs @@ -1,14 +1,26 @@ -using Volo.Abp.Modularity; +using Volo.Abp.BlobStoring; +using Volo.Abp.BlobStoring.Memory; +using Volo.Abp.Modularity; using Volo.Docs.Admin; namespace Volo.Docs { [DependsOn( typeof(DocsAdminApplicationModule), - typeof(DocsDomainTestModule) + typeof(DocsDomainTestModule), + typeof(AbpBlobStoringMemoryModule) )] public class DocsAdminApplicationTestModule : AbpModule { - + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Containers.ConfigureDefault(container => + { + container.UseMemory(); + }); + }); + } } } diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index d6f3bab715..018d62d786 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -158,6 +158,7 @@ $projects = ( "framework/src/Volo.Abp.BlobStoring.Aws", "framework/src/Volo.Abp.BlobStoring.Google", "framework/src/Volo.Abp.BlobStoring.Bunny", + "framework/src/Volo.Abp.BlobStoring.Memory", "framework/src/Volo.Abp.Caching", "framework/src/Volo.Abp.Caching.StackExchangeRedis", "framework/src/Volo.Abp.Castle.Core",