I have a UI designer on the front end which creates a layout.
A layout has rows, and each row has columns, and each column has widgets. The widgets are identified by a key and they also have a config.
public class Layout
{
[Required]
public IEnumerable<Row>? Rows { get; init; }
}
public record Row
{
[Required]
public IEnumerable<Column>? Columns { get; init; }
}
public record Column
{
[Required]
public IEnumerable<Widget>? Widgets { get; init; }
}
public record Widget
{
[Required]
public string? WidgetTypeKey { get; init; }
public object? Config { get; init; }
}
The config of a widget could be any number of C# classes that don't share anything in common. I don't know which one it will be until I determine the widget type by key. So therefore I have used object as the type.
But the config classes still have validation requirements such as [Required], [Range], [MaxLength] and so on.
I can resolve the config class at run time, but I'm not sure how to go about this so that I still get all of the usual ASP.NET validation through the pipeline.
At first I thought I could attach [BindModel] to the Config property or the Widget class and use my own IModelBinder, but these aren't used at all. ASP.NET only considers them if they're at the top of the hierarchy. I.e. the layout. So the model binder is never hit.
I also tried writing an IModelBinderProvider, but again the same problem. The provider is only hit for the initial Layout type, but nothing beyond that. It never queries again for any other type.
I also experimented with generics, thinking that maybe Config could be a TConfig type, but I have no idea how to resolve that at runtime during model binding. Especially since each widget can be a different type.
I guess I could write my own model binder for a layout, but then I miss out on all the automated validation don't I?
Is this too crazy to attempt? Has anyone ever successfully resolved a dynamic object at runtime with a deeply-nested complex type while also letting ASP.NET core do its full validation pipeline?