Hi Engincan,
Thank you for your response.
Let’s keep this ticket open for a few more days while I review the point and run some test scenarios based on your suggestions.
Best regards,
Hi Engincan,
Thank you for your last advice and explanation.
Considering I’m developing a multi-tenant modular monolith application using ABP.io (Blazor Server + EF Core) having the entities and initial CRUD created by ABP Suite, organized into three modules and several entities. My project needs to manage file uploads for multiple entities across these modules, supporting multiple tenants.
From my research and reviewing ABP.io’s default implementation as you mentioned in this support Ticket, I’ve noticed that when a File property is added to an entity, the framework generates a separate file container for each entity, defaulting to the database provider. For my application, this results in many containers and duplicated file management logic within each entity’s AppService.
Instead, I’d like to use Azure BlobStore as the provider, centralizing all file uploads for all tenants into a single container. My plan is to organize files using subdirectories in the following structure: Tenant/Module/Entity/Property This would ensure tenant isolation, logical organization, and easier management—while also keeping things DRY and maintainable.
As I believe the Architeture proposed by ABP represents a good choice to follow, I would appreciate your advice on these points:
1) Recommended Approach in ABP Framework: What is the best practice in ABP to use a single Azure Blob Storage container for all file properties, with subdirectory separation by tenant, module, entity, and property?
2) Centralized Configuration: How can I centralize the file container configuration so that all file uploads (across tenants, modules, and entities) are consistent, easy to maintain, and avoid duplication?
3) Overriding ABP Suite’s Scaffolding: Is there a supported way to override or customize ABP Suite’s default file management scaffolding to generate this centralized logic? If not, what’s the best way to approach it?
4) Manual Implementation and Best Practices: If this structure needs to be implemented manually, what is the recommended way to organize the code/services to keep things DRY, robust, and extensible for future needs?
The documentation is unprecise on these points—especially regarding on centralization of Azure BlobStore configuration and multi modules scenarios. Any code samples, design suggestions, or documentation references would be greatly appreciated.
Thank you very much for your help!
Hi Engincan,
Thank you for your response.
Let’s keep this ticket open for a few more days while I review the point and run some test scenarios based on your suggestions.
Best regards,
I've successfully implemented the configuration for Azure Blob Storage as described in the official documentation. However, my question is more focused on the architectural design required to make this integration scalable and efficient across all modules of my solution.
Specifically, I'm looking to understand:
The documentation provides a good starting point for configuring blob storage, but it doesn't clearly outline how to adapt the ABP Suite-generated file logic to use Azure Blob Storage as the primary storage backend.
I would greatly appreciate any guidance or examples that clarify the best approach to achieve this integration in a clean and scalable manner.
Thank you!
Check the docs before asking a question: https://abp.io/docs/latest Check the samples to see the basic tasks: https://abp.io/docs/latest/samples The exact solution to your question may have been answered before, and please first use the search on the homepage.
Provide us with the following info: 🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration button.
Template: app Created ABP Studio Version: 0.9.25 Current ABP Studio Version: 1.0.1 ABP Suite: 9.2.0 ABP framework: 9.2.1 Tiered: Yes Multi-Tenancy: Yes UI Framework: blazor-server Theme: leptonx Theme Style: system Run Install Libs: Yes Database Provider: ef Database Management System: sqlserver Separate Tenant Schema: Yes Create Initial Migration: Yes Run Db Migrator: Yes Mobile Framework: maui Public Website: Yes Include Tests: Yes Kubernetes Configuration: No Distributed Event Bus: rabbitmq Use Local References: No Optional Modules: GDPR FileManagement TextTemplateManagement LanguageManagement AuditLogging Chat OpenIddictAdmin Selected Languages: English, English (United Kingdom), 简体中文, Español, العربية, हिन्दी, Português (Brasil), Français, Русский, Deutsch (Deuthschland), Türkçe, Italiano, Čeština, Magyar, Română (România), Svenska, Suomi, Slovenčina, Íslenska, 繁體中文
Default Language: English
Create Command: abp new NewApp -t app --tiered --ui-framework blazor-server --mobile maui --database-provider ef --database-management-system sqlserver --theme leptonx --separate-tenant-schema --public-website --without-cms-kit --dont-run-bundling -chat -file-management
Hello!
I am trying to implement the integration of the original File components and implementation generated by Abp Suite with my implementation of Azure Blob Storage and im facing some difficulties in the process.
As abp already implements with the File property type of abp suite, the file is been saved on a container pointing to the local database with its FileDescriptor within the function UploadFileAsync():
I've followed the documentation present in: https://abp.io/docs/latest/framework/infrastructure/blob-storing https://abp.io/docs/latest/framework/infrastructure/blob-storing/azure
However, the documentation does not clearly outline the best approach to replace the default local storage mechanism with Azure Blob Storage, especially while maintaining compatibility with the components generated by ABP Suite.
I would appreciate guidance on the recommended way to redirect file uploads to Azure Blob Storage instead of the local container, while ensuring that metadata and file descriptors continue to be handled correctly by the framework.
Thanks.
Hi EngincanV,
Thanks so much for your prompt response—the workaround worked perfectly. The ABP team truly are experts!
To streamline development, I’m keeping customizations to a minimum and relying on built-in features whenever possible.
Could you please confirm the version in which this fix will be included, and proceed with refunding the credit for this support ticket?
Appreciate your help, and hope you have a great day!
Check the docs before asking a question: https://abp.io/docs/latest Check the samples to see the basic tasks: https://abp.io/docs/latest/samples The exact solution to your question may have been answered before, and please first use the search on the homepage.
Provide us with the following info:
🧐 Hint: If you are using the ABP Studio, you can see all the information about your solution from the configuration window, which opens when you right-click on the solution and click on the Solution Configuration button.
    [19:43:19 INF] Request starting HTTP/2 GET [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js) \- null null
    [19:43:19 INF] The access\_token is active.
    [19:43:19 DBG] Get dynamic claims cache for user: e69f975f-0a44-9f37-92d2-3a1b2258a050
    [19:43:19 INF] Request finished HTTP/2 GET [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js) \- 404 0 null 28\.306ms
    [19:43:19 INF] Request reached the end of the middleware pipeline without being handled by application code. Request path: GET [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js), Response status code: 404
    [19:43:20 INF] Start processing HTTP request GET [https://localhost:44354/api/abp/api-definition](https://localhost:44354/api/abp/api-definition)
    [19:43:20 INF] Sending HTTP request GET [https://localhost:44354/api/abp/api-definition](https://localhost:44354/api/abp/api-definition)
    [19:43:20 WRN] Unhandled exception rendering component: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    TypeError: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    Microsoft.JSInterop.JSException: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    TypeError: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    at Microsoft\.JSInterop\.JSRuntime\.InvokeAsync\[TValue\]\(Int64 targetInstanceId\, String identifier\, Object\[\] args\)
    at Catalog.Blazor.Pages.Catalog.Products.OnAfterRenderAsync(Boolean firstRender) in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 92
    at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
    [19:43:20 ERR] Unhandled exception in circuit 'pD6-ssltpTDcMHVVm\_-6EDmBcqO\_PHTwGURVr-GBUOw'.
    Microsoft.JSInterop.JSException: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    TypeError: Failed to fetch dynamically imported module: [https://localhost:44312/Pages/Products.razor.js](https://localhost:44312/Pages/Products.razor.js)
    at Microsoft\.JSInterop\.JSRuntime\.InvokeAsync\[TValue\]\(Int64 targetInstanceId\, String identifier\, Object\[\] args\)
    at Catalog.Blazor.Pages.Catalog.Products.OnAfterRenderAsync(Boolean firstRender) in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 92
    at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
    [19:43:20 INF] Executed endpoint 'Microsoft.AspNetCore.Routing.RouteEndpoint'
    [19:43:20 INF] Request finished HTTP/2 CONNECT [https://localhost:44312/\_blazor?id=Q970FyOnqG6UQyr7VOT6vw](https://localhost:44312/_blazor?id=Q970FyOnqG6UQyr7VOT6vw) \- 200 null null 3553\.1341ms
    [19:43:20 INF] Executed endpoint '/signalr-hubs/chat'
    [19:43:20 INF] Executed endpoint '/signalr-hubs/chat'
    [19:43:20 INF] Request finished HTTP/1.1 GET [https://localhost:44312/signalr-hubs/chat?id=D7GtX4N-i8j3TvV\_Jj2hBw](https://localhost:44312/signalr-hubs/chat?id=D7GtX4N-i8j3TvV_Jj2hBw) \- 101 null null 3416\.3706ms
    [19:43:20 INF] Request finished HTTP/1.1 GET [https://localhost:44312/signalr-hubs/chat?id=kQqIwGkbkj0dsYVajWGLKA](https://localhost:44312/signalr-hubs/chat?id=kQqIwGkbkj0dsYVajWGLKA) \- 101 null null 3416\.2868ms
    [19:43:20 INF] Connection id "0HNE3U3HKH7SD", Request id "0HNE3U3HKH7SD:00000035": the application completed without reading the entire request body.
    [19:43:20 INF] Received HTTP response headers after 180.5206ms - 200
    [19:43:20 INF] End processing HTTP request after 180.8364ms - 200
    [19:43:20 WRN] Unhandled exception rendering component: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
    System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed.
    at Autofac.Core.Lifetime.LifetimeScope.ThrowDisposedException()
    at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode) at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
    at System.Lazy`1.CreateValue() at Volo.Abp.DependencyInjection.CachedServiceProviderBase.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Volo.Abp.DependencyInjection.AbpLazyServiceProvider.LazyGetRequiredService[T]() at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1.get\_ClientOptions()
    at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1.RequestAsync(ClientProxyRequestContext requestContext) at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1\.RequestAsync\[T\]\(ClientProxyRequestContext requestContext\)
    at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptorClientProxy`1.CallRequestAsync[T](ClientProxyRequestContext requestContext) at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1\.CallRequestAsync\[T\]\(ClientProxyRequestContext context\)
    at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.GetResultAsync(Task task, Type resultType) at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.InterceptAsync(IAbpMethodInvocation invocation)
    at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
    at Castle\.DynamicProxy\.AsyncInterceptorBase\.ProceedAsynchronous\[TResult\]\(IInvocation invocation\, IInvocationProceedInfo proceedInfo\)
    at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync() at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Catalog.Blazor.Pages.Catalog.Products.GetProductsAsync() in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 135 at Catalog.Blazor.Pages.Catalog.Products.OnDataGridReadAsync(DataGridReadDataEventArgs`1 e) in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 169
    at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
    at Blazorise.DataGrid.DataGrid`1.HandleReadData(CancellationToken cancellationToken) at Blazorise.DataGrid.DataGrid`1.HandleReadData(CancellationToken cancellationToken)
    at Blazorise.DataGrid.DataGrid`1.ReloadInternal(CancellationToken cancellationToken) at Blazorise.DataGrid.DataGrid`1.Reload(CancellationToken cancellationToken)
    at Blazorise.DataGrid.DataGrid`1.OnAfterRenderAsync(Boolean firstRender) at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState) [19:43:20 ERR] Unhandled exception in circuit 'pD6-ssltpTDcMHVVm_-6EDmBcqO_PHTwGURVr-GBUOw'. System.ObjectDisposedException: Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it (or one of its parent scopes) has already been disposed. at Autofac.Core.Lifetime.LifetimeScope.ThrowDisposedException() at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
    at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor) at System.Lazy`1.CreateValue()
    at Volo.Abp.DependencyInjection.CachedServiceProviderBase.GetService(Type serviceType)
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
    at Volo.Abp.DependencyInjection.AbpLazyServiceProvider.LazyGetRequiredService[T]()
    at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1.get_ClientOptions() at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1.RequestAsync(ClientProxyRequestContext requestContext)
    at Volo.Abp.Http.Client.ClientProxying.ClientProxyBase`1.RequestAsync[T](ClientProxyRequestContext requestContext) at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptorClientProxy`1\.CallRequestAsync\[T\]\(ClientProxyRequestContext requestContext\)
    at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.CallRequestAsync[T](ClientProxyRequestContext context) at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.GetResultAsync(Task task, Type resultType)
    at Volo.Abp.Http.Client.DynamicProxying.DynamicHttpProxyInterceptor`1.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed) at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo) at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
    at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
    at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
    at Catalog.Blazor.Pages.Catalog.Products.GetProductsAsync() in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 135
    at Catalog.Blazor.Pages.Catalog.Products.OnDataGridReadAsync(DataGridReadDataEventArgs`1 e) in C:\Dev\NewApp\Modules\catalog\src\Catalog.Blazor\Pages\Catalog\Products.razor.cs:line 169 at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task) at Blazorise.DataGrid.DataGrid`1.HandleReadData(CancellationToken cancellationToken)
    at Blazorise.DataGrid.DataGrid`1.HandleReadData(CancellationToken cancellationToken) at Blazorise.DataGrid.DataGrid`1.ReloadInternal(CancellationToken cancellationToken)
    at Blazorise.DataGrid.DataGrid`1.Reload(CancellationToken cancellationToken) at Blazorise.DataGrid.DataGrid`1.OnAfterRenderAsync(Boolean firstRender)
    at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
This following picutures were provided as sample to reproduce the error, the entire application was created using ABP Studio and ABP Suite.
Create a new module named "Catalog" as the picture below:

Follow the importing procedures as ABP documentation:

Create a new Entity Property as File Property Type

Run the application using ABP Studio, but the Blazor Server runs as Terminal to track the error.
After creating the new field named Image as File type, the application hangs as the picture below:

The requested file "Products.razor.js" exists on the Blazor subfolder but the application can't find it.
It seems to have a fail on the ABP Suite template that is creating faulty application.
Hi @m.aliozkaya, The workaround you suggested worked perfectly—thank you! I’ve also gone ahead and addressed similar issues in related child entities. Please keep me posted on when the definitive fix will be available. Once the bug is officially confirmed, I’d appreciate it if the credit consumed by this ticket could be reverted. Thanks a million for your continued support!
Nice! I will update the code on my end and try again, hopping this will fix it.
I'll let you know after my testing here.
Thank you for your assistance.
 
                                