Kit.Core/LibCommon/Kit.Core.Helpers/ControllerExtensions.cs

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;
}
}