多个模型参数在起作用

时间:2019-06-10 03:22:33

标签: asp.net-mvc

我有一个将使用可选的querystring参数调用的动作。但是,这些参数包含在不同的视图模型中。当我尝试将这些模型添加到我的参数列表时,仅填充一个模型,而其他模型始终为空。除了空的查询字符串外,所有模型均使用默认值实例化。

由于我不希望嵌套的属性名称在查询字符串中可见,因此不能嵌套这些模型。因此,除非可以通过某种方式避免这种情况,否则这也是可行的解决方案。

我注意到,在创建DefaultModelBuilder的快速覆盖时,将解析所有模型,但最终结果仍然是实际上只分配了一个模型。

这是我的情况:

public ActionResult Index(ModelA ma, ModelB ba)
{
    return Content("ok");
}
public class ModelA
{
    public string Test { get; set; }
    public string Name { get; set; }
}

public class ModelB
{
    public int? SomeInteger { get; set; }
    public int? TestInteger { get; set; }
}

所需的查询字符串:

index?Test=Hi&SomeInteger=7

我要避免的事情:

index?ModelA.Test=Hi&ModelB.SomeInteger=7

3 个答案:

答案 0 :(得分:0)

您可以尝试结合使用这两个类:

public class ModelPair
{
    public ModelA A { get; set; }
    public ModelB B { get; set; }
}

然后

public ActionResult Index(ModelPair mp)
{
    return Content("ok");
}

您可以进行?A.Test=blah&B.SomeInteger=42

答案 1 :(得分:0)

我最终致力于创建自己的自定义模型活页夹,该活页夹可以进行递归绑定。只要不重复使用属性名(无论如何在我的模型中都不会发生这种情况),就可以解决我不公开嵌套模型类的属性名的问题。

所以现在我有以下类结构:

public class ModelA
{
    public string Test { get; set; }
    public string Name { get; set; }
}

public class ModelB
{
    public int? SomeInteger { get; set; }
    public int? TestInteger { get; set; }
}

public class ViewModel
{
    public ModelA ModelA { get; set; }
    public ModelB ModelB { get; set; }
}

现在动作看起来像这样

public ActionResult Index(ViewModel model)
{
    return Content("ok");
}

在不暴露丑陋的属性名称的情况下,我将使用以下查询字符串:

index?Test=Hi&SomeInteger=7&Name=Yep&TestInteger=72

当然,我还没有对此进行广泛的测试,所以我不知道会出现什么问题,但是现在所有嵌套模型都正确地填充了来自查询字符串的数据,并且可以将模型类易于重复使用:)

public class RecursiveModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = base.BindModel(controllerContext, bindingContext);
        if (model != null)
        {
            var properties = bindingContext.ModelType.GetProperties().Where(x => x.PropertyType.IsClass && !x.PropertyType.Equals(typeof(string)) );
            foreach(var property in properties)
            {
                var resursiveBindingContext = new ModelBindingContext(bindingContext)
                {
                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, property.PropertyType)
                };
                var recursiveModel = BindModel(controllerContext, resursiveBindingContext);
                property.SetValue(model, recursiveModel);
            }
        }
        return model;
    }
}

答案 2 :(得分:-1)

据我所知,默认模型绑定程序无法做到这一点。我们必须实现以下自定义Model Binder。

 public class CustomModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        var query = bindingContext.HttpContext.Request.Query;


        var modelb = new ModelB();
        if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.SomeInteger)}", out var someInteger))
        {
            modelb.SomeInteger = Convert.ToInt32(JsonConvert.DeserializeObject(someInteger).ToString());
        }
        if (query.TryGetValue($"{bindingContext.ModelName}.{nameof(modelb.TestInteger)}", out var testInteger))
        {
            modelb.TestInteger = Convert.ToInt32(JsonConvert.DeserializeObject(testInteger).ToString());
        }

        bindingContext.Result = ModelBindingResult.Success(modelb);
        return Task.FromResult(modelb);
    }
}

在Action控制器中,我们可以按照以下方式使用Binder

   public IActionResult Index(ModelA modelA, [ModelBinder(typeof(CustomModelBinder))]ModelB modelB)
    {
        return Json(new {modelA, modelB});
    }

在querystring中,我们可以使用前缀来区分每个模型。

?modelA.test="MATests"&modelA.Name="modelANameValue"&modelB.SomeInteger="5"

请找到工作示例here on github