Skip to content

Commit ab0fc36

Browse files
authored
Merge branch 'dev' into auto-merge/rel-10-0/4050
2 parents f34e2e0 + 26e78cb commit ab0fc36

File tree

115 files changed

+4832
-52
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+4832
-52
lines changed

.github/workflows/build-and-test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ jobs:
5959
- uses: actions/setup-dotnet@master
6060
with:
6161
dotnet-version: 10.0.x
62-
6362
- name: Build All
6463
run: ./build-all.ps1
6564
working-directory: ./build

Directory.Packages.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@
182182
<PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.6.3" />
183183
<PackageVersion Include="TencentCloudSDK.Sms" Version="3.0.1273" />
184184
<PackageVersion Include="TimeZoneConverter" Version="7.0.0" />
185+
<PackageVersion Include="TickerQ" Version="2.5.3" />
186+
<PackageVersion Include="TickerQ.Dashboard" Version="2.5.3" />
187+
<PackageVersion Include="TickerQ.Utilities" Version="2.5.3" />
188+
<PackageVersion Include="TickerQ.EntityFrameworkCore" Version="2.5.3" />
185189
<PackageVersion Include="Unidecode.NET" Version="2.1.0" />
186190
<PackageVersion Include="xunit" Version="2.9.3" />
187191
<PackageVersion Include="xunit.extensibility.execution" Version="2.9.3" />

abp_io/AbpIoLocalization/AbpIoLocalization/Admin/Localization/Resources/en.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,13 @@
776776
"Menu:Studio": "Studio",
777777
"Menu:Solutions": "Solutions",
778778
"Menu:Users": "Users",
779-
"Menu:UserReports": "Users"
779+
"Menu:UserReports": "Users",
780+
"Enum:TokenType:1": "Free",
781+
"Enum:TokenType:2": "Paid",
782+
"Enum:SourceChannel:1": "Studio",
783+
"Enum:SourceChannel:2": "Support Site",
784+
"Enum:SourceChannel:3": "Suite",
785+
"Menu:OrganizationTokenUsage": "Organization Token Usage",
786+
"Permission:OrganizationTokenUsage": "Organization Token Usage"
780787
}
781788
}

abp_io/AbpIoLocalization/AbpIoLocalization/Www/Localization/Resources/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,7 @@
616616
"QuestionItemErrorMessage": "Could not get the latest question details from Stackoverflow.",
617617
"Oops": "Oops!",
618618
"CreatePostSuccessMessage": "The Post has been successfully submitted. It will be published after a review from the site admin.",
619+
"PostCreationFailed": "An error occurred while creating the post. Please try again later.",
619620
"Browse": "Browse",
620621
"CoverImage": "Cover Image",
621622
"ShareYourExperiencesWithTheABPFramework": "ABP Community Articles | Read or Submit Articles",

common.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
33
<LangVersion>latest</LangVersion>
4-
<Version>10.0.0-rc.1</Version>
5-
<LeptonXVersion>5.0.0-rc.1</LeptonXVersion>
4+
<Version>10.1.0-preview</Version>
5+
<LeptonXVersion>5.1.0-preview</LeptonXVersion>
66
<NoWarn>$(NoWarn);CS1591;CS0436</NoWarn>
77
<PackageIconUrl>https://abp.io/assets/abp_nupkg.png</PackageIconUrl>
88
<PackageProjectUrl>https://abp.io/</PackageProjectUrl>
Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
# Where and How to Store Your BLOB Objects in .NET?
2+
3+
When building modern web applications, managing [BLOBs (Binary Large Objects)](https://cloud.google.com/discover/what-is-binary-large-object-storage) such as images, videos, documents, or any other file types is a common requirement. Whether you're developing a CMS, an e-commerce platform, or almost any other kind of application, you'll eventually ask yourself: **"Where should I store these files?"**
4+
5+
In this article, we'll explore different approaches to storing BLOBs in .NET applications and demonstrate how the ABP Framework simplifies this process with its flexible [BLOB Storing infrastructure](https://abp.io/docs/latest/framework/infrastructure/blob-storing).
6+
7+
ABP Provides [multiple storage providers](https://abp.io/docs/latest/framework/infrastructure/blob-storing#blob-storage-providers) such as Azure, AWS, Google, Minio, Bunny etc. But for the simplicity of this article, we will only focus on the **Database Provider**, showing you how to store BLOBs in database tables step-by-step.
8+
9+
## Understanding BLOB Storage Options
10+
11+
Before diving into implementation details, let's understand the common approaches for storing BLOBs in .NET applications. Mainly, there are three main approaches:
12+
13+
1. Database Storage
14+
2. File System Storage
15+
3. Cloud Storage
16+
17+
### 1. Database Storage
18+
19+
The first approach is to store BLOBs directly in the database alongside your relational data (_you can also store them separately_). This approach uses columns with types like `VARBINARY(MAX)` in SQL Server or `BYTEA` in PostgreSQL.
20+
21+
**Pros:**
22+
- ✅ Transactional consistency between files and related data
23+
- ✅ Simplified backup and restore operations (everything in one place)
24+
- ✅ No additional file system permissions or management needed
25+
26+
**Cons:**
27+
- ❌ Database size can grow significantly with large files
28+
- ❌ Potential performance impact on database operations
29+
- ❌ May require additional database tuning and optimization
30+
- ❌ Increased backup size and duration
31+
32+
### 2. File System Storage
33+
34+
The second obvious approach is to store BLOBs as physical files in the server's file system. This approach is simple and easy to implement. Also, it's possible to use these two approaches together and keep the metadata and file references in the database.
35+
36+
**Pros:**
37+
- ✅ Better performance for large files
38+
- ✅ Reduced database size and improved database performance
39+
- ✅ Easier to leverage CDNs and file servers
40+
- ✅ Simple to implement file system-level operations (compression, deduplication)
41+
42+
**Cons:**
43+
- ❌ Requires separate backup strategy for files
44+
- ❌ Need to manage file system permissions
45+
- ❌ Potential synchronization issues in distributed environments
46+
- ❌ More complex cleanup operations for orphaned files
47+
48+
### 3. Cloud Storage (Azure, AWS S3, etc.)
49+
50+
The third approach can be using cloud storage services for scalability and global distribution. This approach is powerful and scalable. But it's also more complex to implement and manage.
51+
52+
**Best for:**
53+
- Large-scale applications
54+
- Multi-region deployments
55+
- Content delivery requirements
56+
57+
## ABP Framework's BLOB Storage Infrastructure
58+
59+
The ABP Framework provides an abstraction layer over different storage providers, allowing you to switch between them with minimal code changes. This is achieved through the **IBlobContainer** (and `IBlobContainer<TContainerType>`) service and various provider implementations.
60+
61+
> ABP provides several built-in providers, which you can see the full list [here](https://abp.io/docs/latest/framework/infrastructure/blob-storing#blob-storage-providers).
62+
63+
Let's see how to use the Database provider in your application step by step.
64+
65+
### Demo: Storing BLOBs in Database in an ABP-Based Application
66+
67+
In this demo, we'll walk through a practical example of storing BLOBs in a database using ABP's BLOB Storing infrastructure. We'll focus on the backend implementation using the `IBlobContainer` service and examine the database structure that ABP creates automatically. The UI framework choice doesn't matter for this demonstration, as we're concentrating on the core BLOB storage functionality.
68+
69+
If you don't have an ABP application yet, create one using the ABP CLI:
70+
71+
```bash
72+
abp new BlobStoringDemo
73+
```
74+
75+
This command generates a new ABP layered application named `BlobStoringDemo` with **MVC** as the default UI and **SQL Server** as the default database provider.
76+
77+
#### Understanding the Database Provider Setup
78+
79+
When you create a layered ABP application, it automatically includes the BLOB Storing infrastructure with the Database Provider pre-configured. You can verify this by examining the module dependencies in your `*Domain`, `*DomainShared`, and `*EntityFrameworkCore` modules:
80+
81+
```csharp
82+
[DependsOn(
83+
//...
84+
typeof(BlobStoringDatabaseDomainModule) // <-- This is the Database Provider
85+
)]
86+
public class BlobStoringDemoDomainModule : AbpModule
87+
{
88+
//...
89+
}
90+
```
91+
92+
Since the Database Provider is already included through module dependencies, no additional configuration is required to start using it. The provider is ready to use out of the box.
93+
94+
However, if you're working with multiple BLOB storage providers or want to explicitly configure the Database Provider, you can add the following configuration to your `*EntityFrameworkCore` module's `ConfigureServices` method:
95+
96+
```csharp
97+
Configure<AbpBlobStoringOptions>(options =>
98+
{
99+
options.Containers.ConfigureDefault(container =>
100+
{
101+
container.UseDatabase();
102+
});
103+
});
104+
```
105+
106+
> **Note:** This explicit configuration is optional when using only one BLOB provider (Database Provider in this case), but becomes necessary when managing multiple providers or custom container configurations.
107+
108+
#### Running Database Migrations
109+
110+
Now, let's apply the database migrations to create the necessary BLOB storage tables. Run the `DbMigrator` project:
111+
112+
```bash
113+
cd src/BlobStoringDemo.DbMigrator
114+
dotnet run
115+
```
116+
117+
Once the migration completes successfully, open your database management tool and you'll see two new tables:
118+
119+
![](blob-tables.png)
120+
121+
**Understanding the BLOB Storage Tables:**
122+
123+
- **`AbpBlobContainers`**: Stores metadata about BLOB containers, including container names, tenant information, and any custom properties.
124+
125+
- **`AbpBlobs`**: Stores the actual BLOB content (the binary data) along with references to their parent containers. Each BLOB is associated with a container through a foreign key relationship.
126+
127+
When you save a BLOB, ABP automatically handles the database operations: the binary content goes into `AbpBlobs`, while the container configuration and metadata are managed in `AbpBlobContainers`.
128+
129+
#### Creating a File Management Service
130+
131+
Let's implement a practical application service that demonstrates common BLOB operations. Create a new application service class:
132+
133+
```csharp
134+
using System.Threading.Tasks;
135+
using Volo.Abp.Application.Services;
136+
using Volo.Abp.BlobStoring;
137+
138+
namespace BlobStoringDemo
139+
{
140+
public class FileAppService : ApplicationService, IFileAppService
141+
{
142+
private readonly IBlobContainer _blobContainer;
143+
144+
public FileAppService(IBlobContainer blobContainer)
145+
{
146+
_blobContainer = blobContainer;
147+
}
148+
149+
public async Task SaveFileAsync(string fileName, byte[] fileContent)
150+
{
151+
// Save the file
152+
await _blobContainer.SaveAsync(fileName, fileContent);
153+
}
154+
155+
public async Task<byte[]> GetFileAsync(string fileName)
156+
{
157+
// Get the file
158+
return await _blobContainer.GetAllBytesAsync(fileName);
159+
}
160+
161+
public async Task<bool> FileExistsAsync(string fileName)
162+
{
163+
// Check if file exists
164+
return await _blobContainer.ExistsAsync(fileName);
165+
}
166+
167+
public async Task DeleteFileAsync(string fileName)
168+
{
169+
// Delete the file
170+
await _blobContainer.DeleteAsync(fileName);
171+
}
172+
}
173+
}
174+
```
175+
176+
Here, we are doing the followings:
177+
178+
- Injecting the `IBlobContainer` service.
179+
- Saving the BLOB data to the database with the `SaveAsync` method. (_it allows you to use byte arrays or streams_)
180+
- Retrieving the BLOB data from the database with the `GetAllBytesAsync` method.
181+
- Checking if the BLOB exists with the `ExistsAsync` method.
182+
- Deleting the BLOB data from the database with the `DeleteAsync` method.
183+
184+
With this service in place, you can now manage BLOBs throughout your application without worrying about the underlying storage implementation. Simply inject `IFileAppService` wherever you need file operations, and ABP handles all the provider-specific details behind the scenes.
185+
186+
> Also, it's good to highlight that, the beauty of this approach is **provider independence**: you can start with database storage and later switch to Azure Blob Storage, AWS S3, or any other provider without modifying a single line of your application code. We'll explore this powerful feature in the next section.
187+
188+
### Switching Between Providers
189+
190+
One of the biggest advantages of using ABP's BLOB Storage system is the ability to switch providers without changing your application code.
191+
192+
For example, you might start with the [File System provider](https://abp.io/docs/latest/framework/infrastructure/blob-storing/file-system) during development and switch to [Azure Blob Storage](https://abp.io/docs/latest/framework/infrastructure/blob-storing/azure) for production:
193+
194+
**Development:**
195+
```csharp
196+
Configure<AbpBlobStoringOptions>(options =>
197+
{
198+
options.Containers.ConfigureDefault(container =>
199+
{
200+
container.UseFileSystem(fileSystem =>
201+
{
202+
fileSystem.BasePath = Path.Combine(
203+
hostingEnvironment.ContentRootPath,
204+
"Documents"
205+
);
206+
});
207+
});
208+
});
209+
```
210+
211+
**Production:**
212+
```csharp
213+
Configure<AbpBlobStoringOptions>(options =>
214+
{
215+
options.Containers.ConfigureDefault(container =>
216+
{
217+
container.UseAzure(azure =>
218+
{
219+
azure.ConnectionString = "your azure connection string";
220+
azure.ContainerName = "your azure container name";
221+
azure.CreateContainerIfNotExists = true;
222+
});
223+
});
224+
});
225+
```
226+
227+
**Your application code remains unchanged!** You just need to install the appropriate package and update the configuration. You can even use pragmas (for example: `#if !DEBUG`) to switch the provider at runtime (or use similar techniques).
228+
229+
### Using Named BLOB Containers
230+
231+
ABP allows you to define multiple BLOB containers with different configurations. This is useful when you need to store different types of files using different providers. Here are the steps to implement it:
232+
233+
#### Step 1: Define a BLOB Container
234+
235+
```csharp
236+
[BlobContainerName("profile-pictures")]
237+
public class ProfilePictureContainer
238+
{
239+
}
240+
241+
[BlobContainerName("documents")]
242+
public class DocumentContainer
243+
{
244+
}
245+
```
246+
247+
#### Step 2: Configure Different Providers for Each Container
248+
249+
```csharp
250+
Configure<AbpBlobStoringOptions>(options =>
251+
{
252+
// Profile pictures stored in database
253+
options.Containers.Configure<ProfilePictureContainer>(container =>
254+
{
255+
container.UseDatabase();
256+
});
257+
258+
// Documents stored in file system
259+
options.Containers.Configure<DocumentContainer>(container =>
260+
{
261+
container.UseFileSystem(fileSystem =>
262+
{
263+
fileSystem.BasePath = Path.Combine(
264+
hostingEnvironment.ContentRootPath,
265+
"Documents"
266+
);
267+
});
268+
});
269+
});
270+
```
271+
272+
#### Step 3: Use the Named Containers
273+
274+
Once you have defined the BLOB Containers, you can use the `IBlobContainer<TContainerType>` service to access the BLOB containers:
275+
276+
```csharp
277+
public class ProfileService : ApplicationService
278+
{
279+
private readonly IBlobContainer<ProfilePictureContainer> _profilePictureContainer;
280+
281+
public ProfileService(IBlobContainer<ProfilePictureContainer> profilePictureContainer)
282+
{
283+
_profilePictureContainer = profilePictureContainer;
284+
}
285+
286+
public async Task UpdateProfilePictureAsync(Guid userId, byte[] picture)
287+
{
288+
var blobName = $"{userId}.jpg";
289+
await _profilePictureContainer.SaveAsync(blobName, picture);
290+
}
291+
}
292+
```
293+
294+
With this approach, your documents and profile pictures are stored in different containers and different providers. This is useful when you need to store different types of files using different providers and need scalability and performance.
295+
296+
## Conclusion
297+
298+
Managing BLOBs effectively is crucial for modern applications, and choosing the right storage approach depends on your specific needs.
299+
300+
ABP's BLOB Storing infrastructure provides a powerful abstraction that lets you start with one provider and switch to another as your requirements evolve, all without changing your application code.
301+
302+
Whether you're storing files in a database, file system, or cloud storage, ABP's BLOB Storing system provides a flexible and powerful way to manage your files.
7.54 KB
Loading
152 KB
Loading

0 commit comments

Comments
 (0)