using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; using System.Reflection; public static class ControllerExtensions { public static async Task InvokeViewComponentWithModelBindingAsync( this Controller controller, Type viewComponentType) { var viewComponent = GetViewComponentInstance(controller, viewComponentType); var invokeMethod = GetInvokeMethod(viewComponentType); var parameters = invokeMethod.GetParameters(); var parameterValues = await BindParametersAsync(controller, parameters); return await ExecuteViewComponentAsync(viewComponent, invokeMethod, parameterValues); } private static ViewComponent GetViewComponentInstance(Controller controller, Type viewComponentType) { var instance = controller.HttpContext.RequestServices.GetService(viewComponentType) as ViewComponent; if (instance == null) throw new InvalidOperationException($"ViewComponent {viewComponentType.Name} not found."); return instance; } private static MethodInfo GetInvokeMethod(Type viewComponentType) { var method = viewComponentType.GetMethod("InvokeAsync") ?? viewComponentType.GetMethod("Invoke"); if (method == null) throw new InvalidOperationException("ViewComponent does not have Invoke/InvokeAsync method."); return method; } private static async Task BindParametersAsync(Controller controller, ParameterInfo[] parameters) { var parameterValues = new object[parameters.Length]; var modelBindingContexts = new DefaultModelBindingContext[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; var bindingContext = CreateBindingContextAsync(controller, parameter) .GetAwaiter() .GetResult(); var binder = GetModelBinder(controller, parameter); await binder.BindModelAsync(bindingContext); parameterValues[i] = bindingContext.Result.IsModelSet ? bindingContext.Result.Model : parameter.DefaultValue; } return parameterValues; } private static async Task CreateBindingContextAsync(Controller controller, ParameterInfo parameter) { var valueProviders = new List(); var factoryContext = new ValueProviderFactoryContext(controller.ControllerContext); foreach (var factory in controller.ControllerContext.ValueProviderFactories) { await factory.CreateValueProviderAsync(factoryContext); } return new DefaultModelBindingContext { ActionContext = controller.ControllerContext, ModelState = controller.ModelState, ModelMetadata = controller.MetadataProvider.GetMetadataForType(parameter.ParameterType), FieldName = parameter.Name, ModelName = parameter.Name, ValueProvider = new CompositeValueProvider(valueProviders) }; } private static IModelBinder GetModelBinder(Controller controller, ParameterInfo parameter) { var metadata = controller.MetadataProvider.GetMetadataForType(parameter.ParameterType); return controller.ModelBinderFactory.CreateBinder(new ModelBinderFactoryContext { Metadata = metadata, BindingInfo = new BindingInfo { BindingSource = BindingSource.ModelBinding } }); } private static async Task ExecuteViewComponentAsync( ViewComponent viewComponent, MethodInfo method, object[] parameters) { var result = method.Invoke(viewComponent, parameters); if (result is Task task) { await task; return (IViewComponentResult)((dynamic)task).Result; } return (IViewComponentResult)result; } }