106 lines
4.0 KiB
C#
106 lines
4.0 KiB
C#
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<IViewComponentResult> 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<object[]> 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<DefaultModelBindingContext> CreateBindingContextAsync(Controller controller, ParameterInfo parameter)
|
|
{
|
|
var valueProviders = new List<IValueProvider>();
|
|
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<IViewComponentResult> 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;
|
|
}
|
|
} |