Browse Source

low-code: add initializer and hot-reload docs

Update Low-Code docs to introduce a Low-Code Initializer pattern and model.json hot-reload support. Shows using DynamicEntityAssemblyInfo with rootNamespace and projectRootPath, adds a parameter table, and replaces simple assembly registration with an AsyncOneTimeRunner-based InitializeAsync that registers reference entities, assemblies, optional fluent API configs, and calls DynamicModelManager.Instance.InitializeAsync(). Adds a ResolveDomainSourcePath helper, guidance to call the initializer from Program.cs (and DbMigrator/other entry points) before building the app, and notes fallback to embedded resources when projectRootPath is empty.
pull/25021/head
SALİH ÖZKARA 1 month ago
parent
commit
d51e8cb04d
  1. 44
      docs/en/low-code/fluent-api.md
  2. 89
      docs/en/low-code/index.md

44
docs/en/low-code/fluent-api.md

@ -376,14 +376,24 @@ entity.Interceptors.Add(new CommandInterceptorDescriptor
## Assembly Registration
Register assemblies containing `[DynamicEntity]` classes:
Register assemblies containing `[DynamicEntity]` classes in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer):
````csharp
AbpDynamicEntityConfig.SourceAssemblies.Add(
new DynamicEntityAssemblyInfo(typeof(MyDomainModule).Assembly)
new DynamicEntityAssemblyInfo(
typeof(MyDomainModule).Assembly,
rootNamespace: "MyApp",
projectRootPath: sourcePath // For model.json hot-reload
)
);
````
| Parameter | Description |
|-----------|-------------|
| `assembly` | The assembly containing `[DynamicEntity]` classes and/or `model.json` |
| `rootNamespace` | Root namespace for the assembly (used for embedded resource lookup) |
| `projectRootPath` | Path to the Domain project source folder (enables `model.json` hot-reload in development) |
You can also register entity types directly:
````csharp
@ -485,20 +495,34 @@ public class OrderLine
}
````
Register everything in your Domain module:
Register everything in your [Low-Code Initializer](index.md#1-create-a-low-code-initializer):
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
public static class MyAppLowCodeInitializer
{
AbpDynamicEntityConfig.SourceAssemblies.Add(
new DynamicEntityAssemblyInfo(typeof(MyDomainModule).Assembly)
);
// Reference existing ABP entities
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>("UserName");
private static readonly AsyncOneTimeRunner Runner = new();
public static async Task InitializeAsync()
{
await Runner.RunAsync(async () =>
{
// Reference existing ABP entities
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>("UserName");
// Register assembly
AbpDynamicEntityConfig.SourceAssemblies.Add(
new DynamicEntityAssemblyInfo(typeof(MyDomainModule).Assembly)
);
// Initialize
await DynamicModelManager.Instance.InitializeAsync();
});
}
}
````
Then call `await MyAppLowCodeInitializer.InitializeAsync();` in your `Program.cs` before building the application.
This gives you four auto-generated pages (Customers, Products, Orders with nested OrderLines), complete with permissions, menu items, foreign key lookups, and interceptor-based business rules.
## See Also

89
docs/en/low-code/index.md

@ -53,20 +53,95 @@ Run `dotnet ef migrations add Added_Product` and start your application. You get
## Getting Started
### 1. Register Your Assembly
### 1. Create a Low-Code Initializer
In your Domain module, register the assembly that contains your `[DynamicEntity]` classes:
Create a static initializer class in your Domain project's `_Dynamic` folder that registers your assembly and calls `DynamicModelManager.Instance.InitializeAsync()`:
````csharp
public override void ConfigureServices(ServiceConfigurationContext context)
using Volo.Abp.Identity;
using Volo.Abp.LowCode.Configuration;
using Volo.Abp.LowCode.Modeling;
using Volo.Abp.Threading;
namespace MyApp._Dynamic;
public static class MyAppLowCodeInitializer
{
AbpDynamicEntityConfig.SourceAssemblies.Add(
new DynamicEntityAssemblyInfo(typeof(YourDomainModule).Assembly)
);
private static readonly AsyncOneTimeRunner Runner = new();
public static async Task InitializeAsync()
{
await Runner.RunAsync(async () =>
{
// Register reference entities (optional — for linking to existing C# entities)
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>(
nameof(IdentityUser.UserName),
nameof(IdentityUser.Email)
);
// Register assemblies containing [DynamicEntity] classes and model.json
var sourcePath = ResolveDomainSourcePath();
AbpDynamicEntityConfig.SourceAssemblies.Add(
new DynamicEntityAssemblyInfo(
typeof(MyAppDomainModule).Assembly,
rootNamespace: "MyApp",
projectRootPath: sourcePath // Required for model.json hot-reload in development
)
);
// Fluent API configurations (optional — highest priority)
AbpDynamicEntityConfig.EntityConfigurations.Configure("MyApp.Products.Product", entity =>
{
entity.AddOrGetProperty("InternalNotes").AsServerOnly();
});
// Initialize the dynamic model manager
await DynamicModelManager.Instance.InitializeAsync();
});
}
private static string ResolveDomainSourcePath()
{
// Traverse up from bin folder to find the Domain project source
var baseDir = AppContext.BaseDirectory;
var current = new DirectoryInfo(baseDir);
for (int i = 0; i < 10 && current != null; i++)
{
var candidate = Path.Combine(current.FullName, "src", "MyApp.Domain");
if (Directory.Exists(Path.Combine(candidate, "_Dynamic")))
{
return candidate;
}
current = current.Parent;
}
// Fallback for production (embedded resource will be used instead)
return string.Empty;
}
}
````
### 2. Configure DbContext
> The `projectRootPath` parameter enables hot-reload of `model.json` during development. When the path is empty or the file doesn't exist, the module falls back to reading `model.json` as an embedded resource.
### 2. Call the Initializer in Program.cs
The initializer must be called **before** the application starts. Add it to `Program.cs`:
````csharp
public static async Task<int> Main(string[] args)
{
// Initialize Low-Code before building the application
await MyAppLowCodeInitializer.InitializeAsync();
var builder = WebApplication.CreateBuilder(args);
// ... rest of your startup code
}
````
> **Important:** The initializer must also be called in your `DbMigrator` project and any other entry points (AuthServer, HttpApi.Host, etc.) that use dynamic entities. This ensures EF Core migrations can discover the entity schema.
### 3. Configure DbContext
Call `ConfigureDynamicEntities()` in your `DbContext`:

Loading…
Cancel
Save