Skip to main content

Overview

MetadataBinder is the central class in the UI Metadata Framework that holds mappings between server-side types and client-side UI components. It provides APIs to simplify the creation of metadata and manages collections of input and output field bindings.

Namespace

UiMetadataFramework.Core.Binding

Properties

Container
IServiceProvider
Service provider instance used for dependency injection when instantiating components and factories.
Inputs
FieldCollection
Collection of input field bindings. Use this to access and register input components.
Outputs
FieldCollection
Collection of output field bindings. Use this to access and register output components.

Constructors

Default Constructor

public MetadataBinder()
Initializes a new instance using DependencyInjectionContainer.Default as the service provider.

Constructor with Service Provider

public MetadataBinder(
    IServiceProvider container,
    MetadataBinderConfiguration? config = null)
Initializes a new instance with a custom service provider and optional configuration.
container
IServiceProvider
Service provider for dependency injection.
config
MetadataBinderConfiguration?
Optional configuration. If null, default configuration will be used.

Methods

RegisterAssembly

public void RegisterAssembly(Assembly assembly)
Scans an assembly for implementations of ComponentBinding and registers them. This method automatically discovers and registers all component bindings defined in the assembly.
assembly
Assembly
Assembly to scan for component bindings.

GetFormId

public static string GetFormId(Type formType)
Retrives the unique identifier for a form. If the FormAttribute.Id property is specified, it will be used; otherwise, the fully qualified type name is returned.
formType
Type
Type representing the form.
Returns: Unique identifier for the form. Throws: BindingException if the type does not have a FormAttribute.

GetFieldCollection

public FieldCollection GetFieldCollection(string category)
Retrieves the field collection for a specific category.
category
string
Field category (see ComponentCategories).
Returns: FieldCollection for the specified category. Throws: BindingException if no field collection exists for the category.

GetBaseComponent<TAttribute>

public static Type? GetBaseComponent<TAttribute>(Type component)
    where TAttribute : ComponentAttribute
Looks into the inheritance chain of a component and finds the class which has the specified attribute applied directly to it. This is useful for working with derived components.
component
Type
Component class or a class deriving from a component class.
Returns: Class that has the attribute applied directly to it, or null if not found. Throws: BindingException if multi-level derived components are detected.

ComponentCategories

Static class containing standard component category constants:
public static class ComponentCategories
{
    public const string Input = "input";
    public const string Output = "output";
}

Usage Examples

Basic Initialization

using UiMetadataFramework.Core.Binding;

// Create a metadata binder with default settings
var binder = new MetadataBinder();

// Register assemblies containing component bindings
binder.RegisterAssembly(typeof(BasicComponents).Assembly);

Registering Custom Components

// Define a custom component binding
public class CustomTextInputBinding : ComponentBinding
{
    public CustomTextInputBinding()
        : base(
            category: MetadataBinder.ComponentCategories.Input,
            serverType: typeof(string),
            componentType: "custom-text",
            metadataFactory: null)
    {
    }
}

// Register the assembly containing the binding
var binder = new MetadataBinder();
binder.RegisterAssembly(typeof(CustomTextInputBinding).Assembly);

Getting Form Metadata

[Form(Id = "user-profile", Label = "User Profile")]
public class UserProfileForm
{
    [InputField(Required = true)]
    public string FirstName { get; set; }
    
    [InputField(Required = true)]
    public string LastName { get; set; }
    
    [InputField(Required = true)]
    public string Email { get; set; }
}

public class UserProfileResponse
{
    [OutputField(Label = "Profile Updated")]
    public string Message { get; set; }
}

// Generate metadata for the form
var binder = new MetadataBinder();
binder.RegisterAssembly(typeof(UserProfileForm).Assembly);

var formMetadata = binder.BuildForm<UserProfileForm, UserProfileResponse, object>();

// Get form ID
var formId = MetadataBinder.GetFormId(typeof(UserProfileForm));
Console.WriteLine(formId); // Output: "user-profile"

Accessing Input Fields

[Form(Id = "contact")]
public class ContactForm
{
    [InputField(Label = "Name", Required = true, OrderIndex = 1)]
    public string Name { get; set; }
    
    [InputField(Label = "Email", Required = true, OrderIndex = 2)]
    public string Email { get; set; }
    
    [InputField(Label = "Message", Required = true, OrderIndex = 3)]
    public string Message { get; set; }
}

var binder = new MetadataBinder();
binder.RegisterAssembly(typeof(ContactForm).Assembly);

// Get input fields for the form
var inputFields = binder.Inputs.GetFields(typeof(ContactForm));

foreach (var field in inputFields)
{
    Console.WriteLine($"Field: {field.Id}, Type: {field.Type}");
}

Using Custom Service Provider

using Microsoft.Extensions.DependencyInjection;

// Set up dependency injection
var services = new ServiceCollection();
services.AddSingleton<IMyCustomService, MyCustomService>();
var serviceProvider = services.BuildServiceProvider();

// Create binder with custom service provider
var binder = new MetadataBinder(serviceProvider);
binder.RegisterAssembly(typeof(MyForms).Assembly);

Working with Field Collections

var binder = new MetadataBinder();
binder.RegisterAssembly(typeof(MyComponents).Assembly);

// Get input field collection
var inputCollection = binder.GetFieldCollection(MetadataBinder.ComponentCategories.Input);

// Get output field collection
var outputCollection = binder.GetFieldCollection(MetadataBinder.ComponentCategories.Output);

// Or access directly
var inputs = binder.Inputs;
var outputs = binder.Outputs;

Finding Base Components

[Component("base-dropdown")]
public class BaseDropdown<T>
{
    public T Value { get; set; }
    public IEnumerable<T> Options { get; set; }
}

public class CountryDropdown : BaseDropdown<string>
{
    // Additional properties specific to country selection
}

// Find the base component
var baseComponent = MetadataBinder.GetBaseComponent<ComponentAttribute>(typeof(CountryDropdown));
Console.WriteLine(baseComponent.Name); // Output: "BaseDropdown`1"

Complete Example: Building a Form System

using UiMetadataFramework.Core.Binding;
using System.Reflection;

public class FormMetadataService
{
    private readonly MetadataBinder binder;
    
    public FormMetadataService(IServiceProvider serviceProvider)
    {
        binder = new MetadataBinder(serviceProvider);
        
        // Register all assemblies with form definitions
        binder.RegisterAssembly(Assembly.GetExecutingAssembly());
    }
    
    public FormMetadata GetFormMetadata<TRequest, TResponse>()
    {
        return binder.BuildForm<TRequest, TResponse, object>();
    }
    
    public string GetFormId<TRequest>()
    {
        return MetadataBinder.GetFormId(typeof(TRequest));
    }
    
    public IEnumerable<FieldMetadata> GetInputFields<TRequest>()
    {
        return binder.Inputs.GetFields(typeof(TRequest));
    }
    
    public IEnumerable<FieldMetadata> GetOutputFields<TResponse>()
    {
        return binder.Outputs.GetFields(typeof(TResponse));
    }
}

// Usage
var service = new FormMetadataService(serviceProvider);
var metadata = service.GetFormMetadata<LoginForm, LoginResponse>();
var formId = service.GetFormId<LoginForm>();

Registering Multiple Assemblies

var binder = new MetadataBinder();

// Register multiple assemblies
binder.RegisterAssembly(typeof(CoreComponents).Assembly);
binder.RegisterAssembly(typeof(CustomComponents).Assembly);
binder.RegisterAssembly(typeof(ThirdPartyComponents).Assembly);

// Registering the same assembly multiple times is safe - it will be ignored
binder.RegisterAssembly(typeof(CoreComponents).Assembly); // No effect

Notes

  • The MetadataBinder is thread-safe for registration operations.
  • Assemblies are only registered once, even if RegisterAssembly() is called multiple times with the same assembly.
  • Component bindings are automatically discovered from assemblies - you don’t need to manually register each binding.
  • The binder supports dependency injection through the Container property.
  • Form IDs must be unique across your application.

See Also

Build docs developers (and LLMs) love