Reference Entities
Use the Low-Code Designer to select reference entities after they are registered in application startup. This page explains the registration and metadata details behind that designer experience.
Reference Entities allow you to create foreign key relationships from dynamic entities to existing C# entities that live outside the Low-Code System.
If you need the opposite direction, where regular C# code reads or writes low-code entities, see Code Integration.
Dynamic Entities vs Reference Entities
| Dynamic Entities | Reference Entities | |
|---|---|---|
| Definition | Defined via [DynamicEntity] attribute or JSON descriptor files |
Existing C# classes (e.g., IdentityUser, Tenant) |
| CRUD Operations | Full CRUD (Create, Read, Update, Delete) | Read-only — no create/update/delete |
| UI Pages | Auto-generated pages with data grids and forms | No UI pages |
| Permissions | Auto-generated permissions | No permissions |
| Purpose | Primary data management | Foreign key lookups and display values |
| Registration | AbpDynamicEntityConfig.SourceAssemblies |
AbpDynamicEntityConfig.ReferencedEntityList |
Overview
Dynamic entities defined via Attributes or JSON descriptor files can reference other dynamic entities using foreign keys. However, you may also need to link to entities that exist outside the Low-Code System — such as ABP's IdentityUser, Tenant, or your own C# entity classes.
Reference entities make this possible by exposing existing entities for:
- Foreign key lookups — dropdown selection in UI forms
- Display values — showing the entity's display property in grids instead of raw GUIDs
- Read-only queries — querying via the Scripting API
Key distinction: When you define a foreign key with
entityName, the system checks if it's a registered reference entity first. If not found, it assumes it's a dynamic entity.
Registering Reference Entities
Register reference entities in startup configuration using AbpDynamicEntityConfig.ReferencedEntityList:
public static async Task InitializeAsync()
{
await Runner.RunAsync(async () =>
{
// Register reference entity with default display property only
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>(
"UserName"
);
// Register reference entity with additional exposed properties
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>(
"UserName", // Default display property
"UserName", // Exposed properties (for queries and display)
"Email",
"PhoneNumber"
);
// ... rest of initialization
await DynamicModelManager.Instance.InitializeAsync();
});
}
Add<TEntity> Method
public void Add<TEntity>(
string defaultDisplayProperty,
params string[] properties
) where TEntity : class, IEntity<Guid>
| Parameter | Description |
|---|---|
defaultDisplayProperty |
Property name used as display value in lookups |
properties |
Additional properties to expose (optional) |
The entity type must implement
IEntity<Guid>.
Display and Search Properties
Add<TEntity> returns the ReferenceEntityDescriptor, which can declare how a lookup shows and finds records:
AbpDynamicEntityConfig.ReferencedEntityList.Add<IdentityUser>(
"UserName",
"UserName",
"Name",
"Surname",
"Email")
.WithDisplayProperties("Name", "Surname")
.WithSearchProperties("Email");
| Method | Description |
|---|---|
WithDisplayProperties(...) |
The shown value is the non-empty values of these properties joined with a space, for example "John Smith". When all of them are empty, the default display property is shown. Lookups are ordered by the shown value |
WithSearchProperties(...) |
Further properties the lookup search text matches, besides the shown value |
The lookup search matches the default display property, the display properties, and the search properties, ignoring case. When the search text has several words, every word must match one of them. Without display or search properties, only the default display property is searched. The search stays within the lookup's normal page size. All of these must be string properties of the entity; a lookup over a non-string property fails.
Using Reference Entities in JSON Descriptors
Reference a registered entity in a foreign key definition:
{
"name": "UserId",
"foreignKey": {
"entityName": "Volo.Abp.Identity.IdentityUser"
}
}
The entity name must match the CLR type's full name. The module automatically detects that this is a reference entity and uses the registered ReferenceEntityDescriptor.
Using Reference Entities with Attributes
Use the [DynamicForeignKey] attribute on a Guid property:
[DynamicEntity]
public class Customer
{
[DynamicForeignKey("Volo.Abp.Identity.IdentityUser", "UserName")]
public Guid? UserId { get; set; }
}
How It Works
The ReferenceEntityDescriptor class stores metadata about the reference entity:
Name— Full CLR type nameType— The actual CLR typeDefaultDisplayPropertyName— Display property for lookupsDisplayPropertyNames— Properties joined to form the shown value (optional)SearchPropertyNames— Further properties the lookup search matches (optional)Properties— List ofReferenceEntityPropertyDescriptorentries
When a foreign key points to a reference entity, the ForeignKeyDescriptor populates its ReferencedEntityDescriptor and ReferencedDisplayPropertyDescriptor instead of the standard EntityDescriptor fields.
Querying Reference Entities in Scripts
Reference entities can be queried via the Scripting API:
// Query reference entity in interceptor or custom endpoint
var user = await db.get('Volo.Abp.Identity.IdentityUser', userId);
if (user) {
context.log('User: ' + user.UserName);
}
Limitations
- Read-only: Reference entities do not get CRUD operations, permissions, or UI pages.
- No child entities: You cannot define a reference entity as a parent in parent-child relationships.
- Guid keys only: Reference entities must have
Guidprimary keys (IEntity<Guid>). - Explicit registration required: Each reference entity must be registered in code before use.
Common Reference Entities
| Entity | Name for entityName |
Typical Display Property |
|---|---|---|
| ABP Identity User | Volo.Abp.Identity.IdentityUser |
UserName |