Simplify persistence. Embrace scalability. Build the future.
CloudStorageORM is an Entity Framework-style provider that persists entities into cloud object storage.
The current main branch targets .NET 10, uses EF Core 9, and currently ships with Azure Blob Storage and AWS S3 providers.
Support for Google Cloud Storage remains on the roadmap.
- ✅ Targets
net10.0 - ✅ Azure Blob Storage provider is implemented
- ✅ AWS S3 provider is implemented
- ✅ EF-style
DbContextintegration viaUseCloudStorageOrm(...) - ✅ Sample app runs the same CRUD flow against EF InMemory, Azure, and AWS
- ✅ Unit + integration tests run locally with Azurite and LocalStack
- ✅ Coverage collection is wired with Coverlet + ReportGenerator
- 🚧 Google Cloud Storage provider is planned
dotnet add package CloudStorageORMThe repository currently targets .NET 10 SDK.
git clone https://github.com/rzavalik/CloudStorageORM.git
cd CloudStorageORM
dotnet restore CloudStorageORM.slnThe current recommended integration pattern is to configure a regular EF Core DbContext with UseCloudStorageOrm(...).
using CloudStorageORM.Contexts;
using CloudStorageORM.Enums;
using CloudStorageORM.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
: CloudStorageDbContext(options)
{
public DbSet<User> Users => Set<User>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasKey(x => x.Id);
base.OnModelCreating(modelBuilder);
}
}
var services = new ServiceCollection();
services.AddDbContext<AppDbContext>(options =>
{
options.UseCloudStorageOrm(storage =>
{
storage.Provider = CloudProvider.Azure;
storage.ContainerName = "sampleapp-container";
storage.Azure.ConnectionString = "UseDevelopmentStorage=true";
});
});Then use the context with familiar EF operations and LINQ:
await using var provider = services.BuildServiceProvider();
await using var scope = provider.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var user = new User
{
Id = Guid.NewGuid().ToString(),
Name = "John Doe",
Email = "john.doe@example.com"
};
db.Add(user);
await db.SaveChangesAsync();
var users = await db.Set<User>().ToListAsync();
var found = db.Set<User>().FirstOrDefault(x => x.Id == user.Id);
db.Remove(found!);
await db.SaveChangesAsync();Current provider support on
main: Azure Blob Storage and AWS S3.
- The base context namespace is now
CloudStorageORM.Contexts. - Configuration uses composition on
CloudStorageOptions: common fields stay on the root, while provider-specific fields are understorage.Azureandstorage.Aws. CloudStorageOptions.ConnectionStringwas removed; usestorage.Azure.ConnectionStringfor Azure configuration.- Coding style is enforced with file-scoped namespaces (
namespace X;). - The sample app is covered by an integration test that verifies
dotnet runexits successfully. - Integration fixtures can skip Azure/AWS scenarios when Azurite/LocalStack are unavailable.
- CloudStorage transaction support now uses a durable transaction journal under
__cloudstorageorm/tx/<transactionId>/manifest.json. SaveChangesduring an active transaction stages durable operations in the manifest;Commitmarks the manifest as committed and replays operations;Rollbackmarks the transaction as aborted.- Each transaction has a unique
TransactionId(Guid) and only one active transaction is allowed perDbContextinstance. - On startup of a new transaction manager instance, committed manifests are replayed and finalized (
Completed), while pre-commit manifests are marked as aborted (Aborted). - Future versions may add provider-native temporary locking (for example, Azure blob leases and AWS conditional/object-lock strategies) to improve concurrent-writer coordination.
IDatabaseCreatorbehavior is still minimal right now: schema-style database lifecycle methods are not fully implemented because object storage does not map 1:1 to relational database creation semantics.
docker run -d \
-p 10000:10000 \
-p 10001:10001 \
-p 10002:10002 \
--name azurite \
mcr.microsoft.com/azure-storage/azuritedotnet test CloudStorageORM.sln --nologo -v minimaldocker run -d \
-p 4566:4566 \
--name localstack \
-e SERVICES=s3 \
-e AWS_DEFAULT_REGION=us-east-1 \
localstack/localstack:3The integration fixture uses defaults, but you can override them explicitly:
export CLOUDSTORAGEORM_AWS_SERVICE_URL=http://127.0.0.1:4566
export CLOUDSTORAGEORM_AWS_ACCESS_KEY_ID=test
export CLOUDSTORAGEORM_AWS_SECRET_ACCESS_KEY=test
export CLOUDSTORAGEORM_AWS_REGION=us-east-1
export CLOUDSTORAGEORM_AWS_BUCKET=cloudstorageorm-integration-testsdotnet test CloudStorageORM.sln --nologo --settings coverlet.runsettings --collect:"XPlat Code Coverage" -v minimal
dotnet tool restore
dotnet tool run reportgenerator \
-reports:"tests/**/TestResults/*/coverage.cobertura.xml" \
-targetdir:"coverage/report" \
-reporttypes:"Html"The HTML report is generated at coverage/report/index.html.
For CI-equivalent behavior (per-test-project TRX and coverage artifacts), see docs/ci.md.
dotnet run --project samples/CloudStorageORM.SampleApp/SampleApp.csprojThe app runs the same CRUD flow three times:
- Once against EF Core InMemory
- Once against CloudStorageORM configured for Azure Blob Storage / Azurite
- Once against CloudStorageORM configured for AWS S3 / LocalStack
For CloudStorageORM runs, the sample also executes a transaction scenario:
- add entity inside a transaction and
Rollback(entity should not persist) - add entity inside a transaction and
Commit(entity should persist)
See docs/sampleapp.md for details.
- Library documentation
- Sample app guide
- Testing with Azurite
- Testing with LocalStack
- CI workflow and artifacts
- Contributing
- Roadmap
This project is licensed under the GNU General Public License v3.0 (GPL-3.0-or-later). See LICENSE for details.
Contributions are welcome. Please read CONTRIBUTING.md before opening a PR.
CloudStorageORM aims to make cloud object storage feel familiar to EF-oriented .NET applications, while staying explicit about the current provider and platform limits.