Metadata binding is the process of converting C# classes and attributes into metadata that clients can use to render UIs. The MetadataBinder class uses reflection to scan your code, extract attribute information, and build a complete picture of your forms and components.
Decorate your class with ComponentAttribute (or a derived attribute):
[Component(MetadataBinder.ComponentCategories.Output, "my-component")]public class MyComponent{ public string Title { get; set; } public string Description { get; set; }}
When you register the assembly, the MetadataBinder automatically creates a binding.
public static string GetFormId(Type formType){ var attribute = formType.GetTypeInfo().GetCustomAttributeSingleOrDefault<FormAttribute>(); if (attribute == null) { throw new BindingException( $"Type '{formType.FullName}' does not have mandatory " + $"attribute '{typeof(FormAttribute).FullName}'."); } return !string.IsNullOrWhiteSpace(attribute.Id) ? attribute.Id! : formType.FullName ?? throw new BindingException($"Cannot form ID for type `{formType}`.");}
The framework scans request and response classes for properties with field attributes:
// Pseudocode for field discoveryforeach (var property in requestType.GetProperties()){ var inputField = property.GetCustomAttribute<InputFieldAttribute>(); if (inputField != null) { var metadata = CreateFieldMetadata(property, inputField); inputFields.Add(metadata); }}
For each property with InputFieldAttribute or OutputFieldAttribute:
Extract the attribute properties (Label, Required, OrderIndex, etc.)
Determine the component type based on the property type
Here’s the complete flow from C# class to JSON metadata:
1. Developer defines form class with attributes ↓2. RegisterAssembly() scans for component bindings ↓3. FormRegister.RegisterForm() scans form class ↓4. Reflection extracts FormAttribute properties ↓5. Reflection scans Request class for InputFields ↓6. Reflection scans Response class for OutputFields ↓7. For each field: - Get property type - Look up ComponentBinding for that type - Create FieldMetadata with component info - Add custom properties from attributes ↓8. Build complete FormMetadata object ↓9. Serialize to JSON ↓10. Send to client
The FieldCollection manages component bindings for a category:
public class FieldCollection{ public readonly BindingCollection Bindings; public readonly string Category; public FieldMetadata GetFieldMetadata( PropertyInfo property, FieldAttribute? attribute, MetadataBinder binder) { // 1. Get the property type var propertyType = property.PropertyType; // 2. Find the binding for that type var binding = this.Bindings.GetBinding(propertyType); // 3. Create component metadata var component = new Component( type: binding.ComponentType, serverType: propertyType.FullName, configuration: CreateConfiguration(binding, property)); // 4. Create field metadata var field = new FieldMetadata(component) { Id = property.Name, Label = attribute?.Label ?? property.Name, OrderIndex = attribute?.OrderIndex ?? 0, Hidden = attribute?.Hidden ?? false }; return field; }}
var services = new ServiceCollection();services.AddSingleton<IMyService, MyService>();var serviceProvider = services.BuildServiceProvider();var binder = new MetadataBinder(serviceProvider);
Metadata factories and component bindings can request services through the container:
public class MyMetadataFactory : IMetadataFactory{ private readonly IMyService service; public MyMetadataFactory(IMyService service) { this.service = service; } public object? GetMetadata( PropertyInfo property, IComponentBinding binding) { // Use injected service return this.service.GetConfiguration(property); }}
Use dependency injection when your metadata factories need access to configuration, databases, or other services.