我有很多类似的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);
}
答案 0 :(得分:7)
您可以尝试Controller.UpdateModel或Controller.TryUpdateModel方法:
[HttpPost]
public ActionResult SaveStatement(User currentUser, FormCollection statement, string ViewModelName)
{
...
object ViewModel = Activator.CreateInstance(type);
if (TryUpdateModel(viewModel))
{
// save the ViewModel
}
return View(ViewModelName, ViewModel);
}
但是我建议你创建一个自定义的ModelBinder,因为它有责任创建和填充模型属性。
我可以向您展示如何实现这一目标的简单示例:
Base ViewModel
public abstract class StatementViewModel
{
public abstract StatementType StatementType { get; }
...
}
public enum StatementType
{
Relief,
RequestForSalary,
...
}
的ViewModels
public class RequestForSalaryVM : StatementViewModel
{
public override StatementType StatementType
{
get { return StatementType.RequestForSalary; }
}
...
}
public class ReliefVM : StatementViewModel
{
public override StatementType StatementType
{
get { return StatementType.Relief; }
}
...
}
ModelBinder的
public class StatementModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var statementTypeParameter = bindingContext.ValueProvider.GetValue("StatementType");
if (statementTypeParameter == null)
throw new InvalidOperationException("StatementType is not specified");
StatementType statementType;
if (!Enum.TryParse(statementTypeParameter.AttemptedValue, true, out statementType))
throw new InvalidOperationException("Incorrect StatementType"); // not sure about the type of exception
var model = SomeFactoryHelper.GetStatementByType(statementType); // returns an actual model by StatementType parameter
// this could be a simple switch statement
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, model.GetType());
bindingContext.ModelMetadata.Model = model;
return model;
}
}
之后在Global.asax
:
ModelBinders.Binders.Add(typeof(StatementViewModel), new StatementModelBinder());
控制器
[HttpPost]
public ActionResult Index(StatementViewModel viewModel)
{
if (ModelState.IsValid)
{
// save the model
}
return View(viewModel);
}
答案 1 :(得分:2)
您可以使用CustomModelBinder
这样解决问题:
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中注册Custom Binder。
答案 2 :(得分:1)
您是否尝试使用UpdateModel or TryUpdateModel从表单集合初始化模型值?请看下面的代码示例
[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);
}
答案 3 :(得分:0)
如果我是你,我会使用包含视图名称的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之间的差异......