Nano ASP.NET SaaS Boilerplate
Admin credentials (all tenants): admin@email.com / Password123!
Sample data resets every hour
Nano ASP.NET SaaS Boilerplate
General
.NET Solution
Vue UI
React UI
Razor Pages
.NET Solution

Persistence & Infrastructure

The infrastructure layer contains implementations for generic services, which aren’t considered application specific, like identity, mailing, mapping, multi-tenancy, file uploads, and persistence. The implementations are in the infrastructure layer, but their usages is handled by interfaces in the application layer via dependency injection.

Persistence encompasses everything related with storing / retrieving data in a database. An ORM (Object Relational Mapping) is an essential tool which lets you interact with the database using C# objects instead of writing raw SQL queries. The Nano ASP.NET boilerplate uses Entity Framework Core as the ORM.

In Entity Framework, migrations are version control for your database schema. The dbContext classes provide the overall table mapping. Extension methods provide functionality related to multi-tenancy, auditing, and initial seeding. A generic repository handles all the low-level CRUD operations with flexible specifications.

This guide will cover all the infrastructure not already covered in the Authorization & Authentication and Multi-Tenancy guides.

The RepositoryAsync class in Infrastructure/Persistence/Repository is a generic repository responsible for all low-level data operations. It’s implemented via IRepositoryAsync in Application/Common and can be used by application services via dependency injection. RepositoryAsync uses the ApplicationDbContext.

All methods have multiple overrides and can return data as entities or mapped DTOs. Automapper handles DTO mapping, using projection to efficiently map domain entities and related data onto DTOs. The mapping configuration is defined in MappingProfiles in Infrastructure/Mapper.

To query data, any service can pass a specification object to the repository methods which will then be evaluated by Ardalis specification. Specification objects can contain filters, sorting, or any kind of criteria. Using the specification pattern is a great way to encapsulate query logic, making it reusable and easy to maintain.

Pagination is another low-level data operation handled by the repository. The method for returning paginated data, GetPaginatedResultsAsync, differs slightly when using spa or razor as the—ui option, as needed by the front-end data tables.

In a multi-database setup, two DB contexts are used for actual data persistence, BaseDbContext and ApplicationDbContext, and one auxiliary context, TenantDbContext. These contexts are explained in detail in the Multi-Tenancy guide. In a single-database setup, the ApplicationDbContext is the only data persistence context.

Migrations are handled per context found in Infrastructure/Persistence/Migrations. Initial migrations are already created. Any pending migrations will apply automatically on app start.

  • Select Infrastructure as the default project in Package Manager Console
  • Use the add-migration command with -Context to specify DB context and -o to specify output.
  • Running the application will apply any pending migrations. To explicitly run migrations, use the update-database with the -Context switch
add-migration -Context BaseDbContext -o Persistence/Migrations/BaseDb Base-NewMigration
add-migration -Context ApplicationDbContext -o Persistence/Migrations/AppDb App-NewMigration

If using Rider, specify –project AspNano.Infrastructure and -s AspNano.WebApi

  • Select Infrastructure as the default project in Package Manager Console
  • Use the add-migration command with -Context to specify DB context and -o to specify output.
  • Running the application will apply any pending migrations. To explicitly run migrations, use the update-database with the -Context switch
add-migration -Context ApplicationDbContext -o Persistence/Migrations NewMigration

This database seeder class contains the SeedTenantAdminAndRoles method which is run on app start if no root tenant is found in the default database. This method will create a root tenant, roles, and root admin user. A single-tenant setup will create a default admin but no root role or tenant.

Automapper is a third-party NuGet package that provides DTO to domain entity mapping. It requires rules to be defined for each DTO / entity relationship. Add mapping rules in Infrastructure/Mapper/MappingRules when you create new entities and DTOs.

MailKit is a third-party NuGet package that facilitates sending emails in .NET applications. Mail settings are defined in appsettings.json. Sign up for free email testing at Ethereal and replace the provided keys with your own.

The CloudinaryService uses the Cloudinary .NET SDK which is a third-party NuGet package. Cloudinary is a digital asset management platform, which provides programmatic control of images and files on external storage. For the majority of cases, storing images and files on an external service is better than managing them locally. There are many options for managing digital assets; Cloudinary is merely one example, albeit a good one.

Cloudinary settings are found in appsettings.json. If you use Cloudinary, sign up for the free tier and replace the provided keys with your own.

If you’ve purchased the full version with UI projects, continue on to the Vue project or React project documentation to start learning about them. Or, check out the Razor project documentation to see what changes it brings to the .NET solution.