【发布时间】:2022-01-01 08:19:42
【问题描述】:
我正在尝试更改我的规范模型的一个特定属性在 .NET 6 中的绑定方式。我有一堆从 SpecificationBase<> 继承的规范类。有一个int[]? Ids { get; set; } 属性是基类的一部分。我创建了一个自定义活页夹来获取 CSV 数字字符串并将它们转换为 int[]?大批。 (注意:我知道数组可以以?Ids=1&Ids3=&Ids=5 的形式传递给控制器操作,这将正确绑定到数组)。
在我在网上看到的旧示例中,自定义模型绑定器继承自 DefaultModelBinder。然后我们可以使用base.BindModel 或base.BindProperty。对于 .Net 6,我找不到任何类似的东西。有一个 ComplexObjectModelBinder,但它是密封的。
只要规范属性是简单类型,下面的一切都可以正常工作。 TypeDescriptor 转换似乎对那些人很有效。我担心什么时候有复杂的类型。除了我的Ids 属性之外,有没有办法回退到默认绑定?
public class SpecificationModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
ArgumentNullException.ThrowIfNull(bindingContext);
// If not a specification, skip
if (bindingContext.ModelType.BaseType?.GetGenericTypeDefinition() != typeof(SpecificationBase<>))
return Task.CompletedTask;
// Type of specification
var type = bindingContext.ModelType;
// Create default instance of the specification
var model = Activator.CreateInstance(type);
// We are passing the specification parameters via the querystring, so loop over the querystring keys to set specification properties
foreach (var name in bindingContext.HttpContext.Request.Query.Keys)
{
// Check to make sure there is a matching property. If not continue.
var property = type.GetProperty(name);
if (property is null)
continue;
if (property.Name == "Ids")
{
// Custom binding to convert int csv string into int[] array
string? idsString = bindingContext.ValueProvider.GetValue("Ids").FirstValue;
int[]? ids = null;
if (!string.IsNullOrEmpty(idsString))
ids = idsString.Split(',', StringSplitOptions.RemoveEmptyEntries).Where(x => int.TryParse(x, out _)).Select(x => int.Parse(x)).ToArray();
property.SetValue(model, ids);
}
else
{
// Is there a standard way to bind all of the other properties? This will only handle simple types
var value = bindingContext.ValueProvider.GetValue(property.Name).FirstValue;
var converter = TypeDescriptor.GetConverter(property.PropertyType);
var convertedValue = converter.ConvertFrom(value!);
property.SetValue(model, convertedValue);
}
}
// Set the result to our populated model
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
【问题讨论】:
-
也许值得为该属性创建特殊的活页夹并用它标记所有
Ids属性?您也可以尝试创建IModelBinderProvider,它将检查属性类型和名称并返回您的自定义活页夹(它应该只处理属性)。 -
大师,规范在一个单独的程序集中,不依赖于
Microsoft.AspNetCore,所以我不确定我能否使您的第一个建议奏效。关于第二个建议,我只希望对派生自SpecificationBase<>的规范的Ids属性进行自定义处理。我正在尝试通过在我的控制器中执行public async Task<IActionResult> List([ModelBinder(typeof(SpecificationModelBinder))] CountrySpecification specification)之类的操作来明确选择加入活页夹。
标签: c# asp.net-core asp.net-core-webapi model-binding .net-6.0