Attribute Groups and Ordering
When a schema contains many attributes, displaying them as a flat list quickly becomes hard to navigate. Attribute groups let you organize attributes into named sections and control the order in which both groups and individual attributes appear. This is especially useful when building your own admin UIs or profile editors that need to present attributes in a structured, logical way.
AttributeGroup
Section titled “AttributeGroup”An AttributeGroup represents a named section that attributes can be assigned to.
public sealed record AttributeGroup( AttributeGroupCode Code, AttributeDisplayName? DisplayName, AttributeDescription? Description, int Order);Code: The unique identifier for the group. SeeAttributeGroupCodebelow.DisplayName: Optional human-readable label you can show in UIs instead of the raw code.Description: Optional description of what the group contains.Order: Sort weight controlling the position of this group relative to other groups. Lower values appear first.
AttributeGroupCode
Section titled “AttributeGroupCode”AttributeGroupCode is a string-based identifier for a group. Valid characters are alphanumeric, dashes, and underscores. Comparison is case-insensitive.
Create an AttributeGroupCode using the static Create method:
var code = AttributeGroupCode.Create("personal-info");AttributeDefinition group properties
Section titled “AttributeDefinition group properties”Two properties on AttributeDefinition control how an attribute is placed within the group structure:
AttributeGroupCode? GroupCode: The group this attribute belongs to.nullmeans the attribute is ungrouped and appears outside any group section.int Order: Sort weight controlling the display position of this attribute within its group (or among ungrouped attributes). Lower values appear first.
These properties are set when constructing an AttributeDefinition. To change them, build a new SchemaConfiguration (or modify one read via ISchemaAdmin) with the updated definitions — there is no dedicated reorder operation. See Schema management for the Extend and get-modify-save patterns.
Managing Groups
Section titled “Managing Groups”Groups are part of the SchemaConfiguration.Groups collection, alongside AttributeDefinitions. There is no dedicated API for adding, removing or reordering groups and attributes — you build the ICollection<AttributeGroup> and set each AttributeDefinition.GroupCode/Order as part of the schema you register, the same way you manage any other part of the schema:
- For an in-memory schema, include the groups and ordered definitions when you call
BuiltInSchemas.UserProfile.Extend(attributes, groups)or construct a fullSchemaConfiguration, and register it withAddInMemoryDataExtensionSchemas. - For a storage-backed schema, use the
ISchemaAdminget-modify-save pattern: read the currentSchemaConfiguration, add to or reorder itsGroupsandAttributeDefinitionscollections, then callUpdateAsyncwith the version from the read.
Setting Up Groups
Section titled “Setting Up Groups”The following example extends the built-in user profile with a work-info group and two new attributes assigned to it, in a chosen order.
The example adds new attributes rather than grouping existing ones: the built-in profile already defines email, name, given_name and family_name, and Extend(...) throws an InvalidOperationException if an attribute code already exists in the schema. To put built-in attributes in a group, register a full replacement schema instead (see Profiles).
using Duende.Storage.EntityAttributeValue;using Duende.UserManagement.Profiles;
var workInfo = new AttributeGroup( Code: AttributeGroupCode.Create("work-info"), DisplayName: AttributeDisplayName.Create("Work Information"), Description: null, Order: 0);
var jobTitle = new AttributeDefinition{ Code = AttributeCode.Create("job_title"), AttributeType = new ScalarAttributeType(ScalarDataType.String), GroupCode = workInfo.Code, Order = 1};
var department = new AttributeDefinition{ Code = AttributeCode.Create("department"), AttributeType = new ScalarAttributeType(ScalarDataType.String), GroupCode = workInfo.Code, Order = 0};
var extended = BuiltInSchemas.UserProfile.Extend([jobTitle, department], [workInfo]);builder.Services .AddIdentityServer() .AddUserManagement(_ => { }) .AddInMemoryDataExtensionSchemas([extended]);Because department.Order (0) is lower than jobTitle.Order (1), a UI that reads schema.AttributeDefinitions ordered by Order within the work-info group renders department before job title.
To change ordering later for a storage-backed schema, read the schema with ISchemaAdmin.GetAsync, update the Order values on the definitions or groups you want to move, and call UpdateAsync with the returned version:
var getResult = await schemaAdmin.GetAsync(SchemaId.UserProfile, ct);if (getResult.Found){ var schema = getResult.Item; foreach (var definition in schema.AttributeDefinitions) { if (definition.Code == AttributeCode.Create("job_title")) { // AttributeDefinition is a record; replace it in the collection with an updated copy. schema.AttributeDefinitions.Remove(definition); schema.AttributeDefinitions.Add(definition with { Order = 0 }); break; } }
await schemaAdmin.UpdateAsync(SchemaId.UserProfile, schema, getResult.Version!, ct);}Notes on Ordering
Section titled “Notes on Ordering”Order values do not need to be unique. When two attributes share the same Order value, apply your own stable secondary sort (for example, by code) when rendering a UI.
Removing a group from the schema does not fail if attributes still reference it by GroupCode — treat that as an application-level validation concern when you build the replacement schema: either reassign those attributes’ GroupCode to null or to another existing group before removing the group, so you don’t end up with attributes pointing at a group that no longer exists.