Multi-Tenant Application Design

What Is a Multi-Tenant Application A multi-tenant application is a software architecture design that allows a single instance of software to serve multiple customers. Each customer is called a tenant; data across tenants is automatically isolated, so one tenant’s data never affects another’s. Search for “multi-tenancy” on the ZeroDegree Programming website to watch the multi-tenant design video tutorial. Isolating Tenant Data with a Shared Table and a Discriminator Column The shared-table multi-tenant design stores data for multiple tenants in a single table, distinguishing each tenant’s rows with an added TenantId column. ...

March 10, 2026

Orchestration with the Aspire App Host

Adding Application Components via Add Methods Using methods such as AddProject, AddContainer, AddExecutable, AddParameter, and AddConnectionString, you can orchestrate the app host into a complete application. Configuring Explicit Resource Startup builder.AddProject<Projects.MyApp>("dbmigration").WithExplicitStart(); Using the WithExplicitStart method, you can configure explicit resource startup; resources configured for explicit startup need to be started manually. Referencing Resources with the WithReference Method builder.AddProject<Projects.MyApp>("myapp").WithReference(postgresdb); The referenced resource can be an endpoint, a connection string, or another resource. The referenced resource is passed to the referencing resource via environment variables. ...

March 10, 2026

Ordering Microservice Architecture Overview

The ordering part covers many technical topics, including the CQRS pattern, MediatR for local command dispatch, local event handling, abstraction and implementation of distributed events, the outbox pattern, distributed locks, and more. Ordering Service Architecture Diagram Microservice Communication Client Side The client side mainly covers order creation, payment, cancellation, and querying. The client calls the ordering microservice through the API Gateway; the ordering microservice processes commands via MediatR, then publishes events through the event bus; the event bus dispatches the events to event handlers, which process them and then persist the events to the database. ...

March 10, 2026

Overview of New Features in .NET 10

Library Improvements Numeric Ordering for String Comparison In .NET 10, the System.String class adds a CompareAsNumbers method for comparing strings as numbers. This is very useful for sorting strings that contain numbers, such as file names or version numbers. StringComparer numericStringComparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering); Console.WriteLine(numericStringComparer.Equals("02", "2")); // Output: True foreach (string os in new[] { "Windows 8", "Windows 10", "Windows 11" }.Order(numericStringComparer)) { Console.WriteLine(os); } // Output: // Windows 8 // Windows 10 // Windows 11 HashSet<string> set = new HashSet<string>(numericStringComparer) { "007" }; Console.WriteLine(set.Contains("7")); // Output: True UTF-8 Support for Hexadecimal String Conversion .NET 10 adds UTF-8 support for hexadecimal string conversion operations in the Convert class. These new methods provide an efficient way to convert between UTF-8 byte sequences and hexadecimal representations without intermediate string allocations: ...

March 10, 2026

Paging, Sorting, and Multi-Condition Queries

Paging Parameters Requests should use the GET method http://localhost:8080/api/products?keyword=test&pagenumber=1&pagesize=5&orderby=id desc,price asc The response is as follows { "totalCount": 100, "items": [ { "id": 1, "name": "test", "price": 100 }, { "id": 2, "name": "test", "price": 200 } ] } Paged Request Model public class PagedAndSortedRequest : PagedRequest { public string? OrderBy { get; init; } } Paged Response Model public class PagedResponse<T>(IReadOnlyList<T> items, int totalCount) { public IReadOnlyList<T> Items { get; init; } = items; public int TotalCount { get; init; } = totalCount; } Extending IQueryable to Sort by Property Name QueryableOrderByExtensions.cs Convert a string condition into a sort expression IOrderedQueryable<TSource> OrderBy(IQueryable<TSource> source, string propertyName) Extending IQueryable for Searching and Sorting QueryableExtensions.cs Extension methods for paging and sorting ...

March 10, 2026

Policy-based and Resource-based Authorization

After authentication, identity data is available in HttpContext.User. However, identity is not equal to permission. Permissions should be evaluated by the authorization system. Authorization in ASP.NET Core ASP.NET Core provides a policy-based authorization model. Policies can be defined by role, resource, or custom conditions. Use IAuthorizationService IAuthorizationService is the core authorization service in ASP.NET Core: public interface IAuthorizationService { Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable<IAuthorizationRequirement> requirements); Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName); Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, AuthorizationPolicy policy); } AuthorizationResult contains success/failure information and reasons. Implement a custom policy provider You can use OperationAuthorizationRequirement to model permission requirements and inject them into policies. ...

March 10, 2026

PostgreSQL High Availability and Read/Write Splitting

Basic Concepts A single operating system can run multiple PostgreSQL instances, each with its own configuration file, data directory, and port. Each instance can host multiple databases, and each database can contain multiple tables. Docker makes it easy to create multiple PostgreSQL instances. Installing the Primary and Replica Servers docker run --name postgres1 -e POSTGRES_PASSWORD=postgres -d -p 5431:5432 -v ${pwd}/postgres1/data:/var/lib/postgresql/data postgres docker run --name postgres2 -e POSTGRES_PASSWORD=postgres -d -p 5432:5432 -v ${pwd}/postgres2/data:/var/lib/postgresql/data postgres Logical Replication Logical replication is a method of replicating data objects and their changes based on a replication identity (usually the primary key). We use the term “logical” in contrast to physical replication. Logical replication uses a publish and subscribe model, in which one or more subscribers subscribe to one or more publications on a publisher node. Subscribers pull data from the publications they subscribe to, and may subsequently re-publish the data to allow cascading replication or more complex configurations. ...

March 10, 2026

Provider used to generate initial data

Data seeding is the process of populating a database with a set of initial data. It is usually performed when the database is first created. These data are usually static and will not change over time. Data seeding is usually used to fill in some basic data, such as users, roles, permissions, products, classifications, etc. Universal interface design public interface IDataSeedingProvider { Task SeedingAsync(IServiceProvider serviceProvider); } Implement specific interface public class DataSeedingProvider(UserManager<User> userManager) : IDataSeedingProvider { public async Task SeedingAsync(IServiceProvider ServiceProvider) { var user = await userManager.FindByNameAsync("admin"); if (user == null) { await userManager.CreateAsync(new User { UserName = "admin", Email = "admin@test.com" }, "admin"); } } } Register in the container builder.Services.AddTransient<IDataSeedingProvider, DataSeedingProvider>(); Automated execution of extensions public static class DataSeedingExtensions { public static IApplicationBuilder UseDataSeedingProviders(this IApplicationBuilder app) { using var serviceScope = app.ApplicationServices.CreateScope(); var dataSeedingProviders = serviceScope.ServiceProvider.GetServices<IDataSeedingProvider>(); foreach (IDataSeedingProvider dataSeedingProvider in dataSeedingProviders) { dataSeedingProvider.SeedingAsync(serviceScope.ServiceProvider).Wait(); } return app; } } Automatic registration implementation public static class DataSeedingExtensions { public static IServiceCollection AddDataSeedingProviders(this IServiceCollection services, Assembly? assembly = null) { assembly ??= Assembly.GetCallingAssembly(); var dataSeedProviders = assembly.ExportedTypes.Where(t => t.IsAssignableTo(typeof(IDataSeedingProvider)) && t.IsClass); dataSeedProviders.ToList().ForEach(t => services.AddTransient(typeof(IDataSeedingProvider), t)); return services; } } Execution order problem The execution order of automatically registered providers is uncertain. If you need to determine the execution order, you can design the Order attribute, and then order execution according to the Order attribute in the UseDataSeedingProviders method. ...

March 10, 2026

RabbitMQ Management

rabbitmq-plugins enable rabbitmq_management Default User:guest/guest Add User:test/test http://192.168.0.202:15672 http://mqtt.test:15672 rabbitmq-plugins enable rabbitmq_mqtt rabbitmq-plugins disable rabbitmq_mqtt rabbitmq-plugins enable rabbitmq_web_mqtt rabbitmq-plugins disable rabbitmq_web_mqtt rabbitmq-plugins enable_feature_flag all rabbitmq-plugins disable_feature_flag all rabbitmq-plugins enable rabbitmq_auth_backend_http Modify the rabbitmq.conf configuration file, located at %AppData%\RabbitMQ auth_backends.1 = http auth_http.http_method = post auth_http.user_path = http://some-server/api/RabbitMqAuth/User auth_http.vhost_path = http://some-server/api/RabbitMqAuth/Vhost auth_http.resource_path = http://some-server/api/RabbitMqAuth/Resource auth_http.topic_path = http://some-server/api/RabbitMqAuth/Topic RabbitMQ server to perform authentication

March 10, 2026

Readme

Friends, the new generation of the Zero framework officially starts being built today. We are very excited to announce that the new framework is named the HelloShop project. This framework will demonstrate a development architecture built on the new generation of the .NET technology stack. The name HelloShop was chosen because a simple shop application can demonstrate all the technologies of a system. Of course, this shop system could just as well be any other system — as long as you understand the design philosophy of this framework, you can quickly build a system of your own. The following document was generated by AI and may contain some grammatical errors, which we will correct later. ...

March 10, 2026