如何解决泛型类型参数的多态模型绑定
我要随文件一起发送部分对象。
var formData = new FormData();
formData.append('model',JSON.stringify({ ... }));
formData.append('file',file,file.name)
// send formData to backend
我的控制器方法使用来自 OData 的 Delta<T>
。
[HttpPatch("{id}")]
public async Task<IActionResult> Patch(
[FromRoute] Guid id,[ModelBinder(BinderType = typeof(FormDataJsonBinder))] Delta<AbstractModel> model,IFormFile file = null
)
但是我不会发送 Delta<AbstractModel>
,因为 AbstractModel 是一个抽象类。所以我真的要发送 Delta<DerivedModel>
或 Delta<AnotherDerivedModel>
等...
我的问题是 ASP.NET 不断抛出说它不能转换为 Delta<DerivedModel>
System.InvalidCastException: Unable to cast object of type 'Microsoft.AspNet.OData.Delta`1[DerivedModel]' to type 'Microsoft.AspNet.OData.Delta`1[AbstractModel]'.
at lambda_method308(Closure,Object,Object[] )
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper,ObjectMethodExecutor executor,Object controller,Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker,ValueTask`1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker,Task lastTask,State next,Scope scope,Object state,Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next,Scope& scope,Object& state,Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker,Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker,Boolean isCompleted)
这是我的 FormDataJsonBinder
public class FormDataJsonBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// Fetch the value of the argument by name and set it to the model state
string fieldName = bindingContext.FieldName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);
if (valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
else bindingContext.ModelState.SetModelValue(fieldName,valueProviderResult);
// Do nothing if the value is null or empty
string value = valueProviderResult.FirstValue;
if (string.IsNullOrEmpty(value)) return Task.CompletedTask;
try
{
// Delta is a special case
if (bindingContext.ModelType.IsAssignableTo(typeof(Delta)))
{
var jsonObject = JObject.Parse(value);
// This extracts type T from Delta<T>
var innerModelType = bindingContext.ModelType.GenericTypeArguments.First();
if (jsonObject["@odata.type"] is not null)
{
var odataType = jsonObject["@odata.type"].Value<string>();
innerModelType = Type.GetType(odataType);
}
var innerModel = JsonConvert.DeserializeObject(value,innerModelType);
var deltaType = typeof(Delta<>).MakeGenericType(innerModelType);
var delta = Activator.CreateInstance(deltaType) as IDelta;
foreach (var property in jsonObject.Properties())
{
delta.TrySetPropertyValue(property.Name,innerModel.GetType().GetProperty(property.Name)?.GetValue(innerModel));
}
bindingContext.Result = ModelBindingResult.Success(delta);
}
else
{
// Deserialize the provided value and set the binding result
var result = JsonConvert.DeserializeObject(value,bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
}
catch (JsonException)
{
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
我也尝试将 IModelBinderProvider
用作 recommended here,但这也只是抛出“无法投射到 Delta<DerivedModel>
”
解决方法
解决方案是以正确的方式创建 Delta 对象。
var delta = Activator.CreateInstance(bindingContext.ModelType,innerModelType) as IDelta;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。