C# 来自FormCollection的asp.net mvc填充视图模型

C# 来自FormCollection的asp.net mvc填充视图模型,c#,asp.net,asp.net-mvc,model-binding,C#,Asp.net,Asp.net Mvc,Model Binding,我有很多类似的ViewModel: public class RequestForSalaryVM : StatementViewModel { // RequestForSalaryVM properties } public class ReliefVM : StatementViewModel { // ReliefVM properties } 还有很多类似的方法: [HttpPost] public ActionResult SaveRelief(User currentU

我有很多类似的ViewModel:

public class RequestForSalaryVM : StatementViewModel
{
  // RequestForSalaryVM properties
}

public class ReliefVM : StatementViewModel
{
  // ReliefVM properties
}
还有很多类似的方法:

[HttpPost]
public ActionResult SaveRelief(User currentUser, ReliefVM statement)
{
    ReliefVM model = (ReliefVM)SaveModel(currentUser, statement);
    if (model == null)
        return RedirectToAction("List");
    return View("Relief", model);
}

[HttpPost]
public ActionResult SaveRequestForSalary(User currentUser, RequestForSalaryVM statement)
{
    RequestForSalaryVM model = (RequestForSalaryVM)SaveModel(currentUser, statement);
    if (model == null)
        return RedirectToAction("List");
    return View("RequestForSalary", model);
}
我想得到这样的东西:

[HttpPost]
public ActionResult SaveStatement(User currentUser, FormCollection statement, string ViewModelName)
{
  Assembly assembly = typeof(SomeKnownType).Assembly;
  Type type = assembly.GetType(ViewModelName);
  object ViewModel = Activator.CreateInstance(type);

  //Fill ViewModel from FormCollection  <= how can I use asp.net mvc binding for this?
  //I do not want to create their own implementation of asp.net mvc binding 
    return View(ViewModelName, ViewModel);
}
public class StatementVMBinder : DefaultModelBinder
{
    // this is the only method you need to override:
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType == typeof(StatementViewModel)) // so it will leave the other VM to the default implementation.
        {
            // this gets the value from the form collection, if it was in an input named "ViewModelName":
            var discriminator = bindingContext.ValueProvider.GetValue("ViewModelName");
            Type instantiationType;
            if (discriminator == "SomethingSomething")
                instantiationType = typeof(ReliefVM);
            else // or do a switch case
                instantiationType = typeof(RequestForSalaryVM);

            var obj = Activator.CreateInstance(instantiationType);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
            bindingContext.ModelMetadata.Model = obj;
            return obj;
        }
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}
[HttpPost]
public ActionResult SaveStatement(用户currentUser、FormCollection语句、字符串ViewModelName)
{
Assembly Assembly=类型(SomeKnownType).Assembly;
Type=assembly.GetType(ViewModelName);
对象ViewModel=Activator.CreateInstance(类型);

//从FormCollection填充ViewModel如果我是你,我会使用DTO(数据传输对象)包装视图的名称和通过接口访问的ViewModel

[HttpPost]
public ActionResult SaveStatement(User currentUser, VMWrapper wrapper)
{
    IVM model = SaveModel(currentUser, wrapper.Statement);
    if (model == null)
        return RedirectToAction("List");
    return View(wrapper.ViewName, model);
}
但这假设您的视图可以处理VM之间的差异…

您可以尝试或使用以下方法:

但是,我建议您创建自定义ModelBinder,因为它负责创建和填充模型属性

我可以向您展示一个简单的示例,说明如何实现这一点:

基本视图模型

视图模型

模型装订机

然后在
Global.asax
中注册模型绑定器:

ModelBinders.Binders.Add(typeof(StatementViewModel), new StatementModelBinder());
控制器

您是否尝试使用初始化表单集合中的模型值?请查看下面的代码示例

[HttpPost]
public ActionResult SaveStatement(User currentUser, FormCollection statement, string    ViewModelName)
{
  Assembly assembly = typeof(SomeKnownType).Assembly;
  Type type = assembly.GetType(ViewModelName);
  object ViewModel = Activator.CreateInstance(type);

  if (!TryUpdateModel(ViewModel, statement.ToValueProvider()))
  {
     //some another actions
  }

  return View(ViewModelName, ViewModel);
}

您可能可以使用
CustomModelBinder
解决此问题,如下所示:

[HttpPost]
public ActionResult SaveStatement(User currentUser, FormCollection statement, string ViewModelName)
{
  Assembly assembly = typeof(SomeKnownType).Assembly;
  Type type = assembly.GetType(ViewModelName);
  object ViewModel = Activator.CreateInstance(type);

  //Fill ViewModel from FormCollection  <= how can I use asp.net mvc binding for this?
  //I do not want to create their own implementation of asp.net mvc binding 
    return View(ViewModelName, ViewModel);
}
public class StatementVMBinder : DefaultModelBinder
{
    // this is the only method you need to override:
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType == typeof(StatementViewModel)) // so it will leave the other VM to the default implementation.
        {
            // this gets the value from the form collection, if it was in an input named "ViewModelName":
            var discriminator = bindingContext.ValueProvider.GetValue("ViewModelName");
            Type instantiationType;
            if (discriminator == "SomethingSomething")
                instantiationType = typeof(ReliefVM);
            else // or do a switch case
                instantiationType = typeof(RequestForSalaryVM);

            var obj = Activator.CreateInstance(instantiationType);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
            bindingContext.ModelMetadata.Model = obj;
            return obj;
        }
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}
您的操作需要此签名:

public ActionResult SaveStatement(User currentUser, StatementViewModel viewModel)
但是,您在方法中收到的
viewModel
将是适当的派生类型,因此您应该能够像在单个方法中一样强制转换它

剩下的就是在Global.asax中注册自定义绑定器

[HttpPost]
public ActionResult SaveStatement(User currentUser, FormCollection statement, string    ViewModelName)
{
  Assembly assembly = typeof(SomeKnownType).Assembly;
  Type type = assembly.GetType(ViewModelName);
  object ViewModel = Activator.CreateInstance(type);

  if (!TryUpdateModel(ViewModel, statement.ToValueProvider()))
  {
     //some another actions
  }

  return View(ViewModelName, ViewModel);
}
public class StatementVMBinder : DefaultModelBinder
{
    // this is the only method you need to override:
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType == typeof(StatementViewModel)) // so it will leave the other VM to the default implementation.
        {
            // this gets the value from the form collection, if it was in an input named "ViewModelName":
            var discriminator = bindingContext.ValueProvider.GetValue("ViewModelName");
            Type instantiationType;
            if (discriminator == "SomethingSomething")
                instantiationType = typeof(ReliefVM);
            else // or do a switch case
                instantiationType = typeof(RequestForSalaryVM);

            var obj = Activator.CreateInstance(instantiationType);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, instantiationType);
            bindingContext.ModelMetadata.Model = obj;
            return obj;
        }
        return base.CreateModel(controllerContext, bindingContext, modelType);
    }
}
public ActionResult SaveStatement(User currentUser, StatementViewModel viewModel)