2014-01-29 10 views
7

ich viele ähnliche Ansichtsmodell haben:asp.net Mvc füllen Ansichtsmodell von Formcollection

public class RequestForSalaryVM : StatementViewModel 
{ 
    // RequestForSalaryVM properties 
} 

public class ReliefVM : StatementViewModel 
{ 
    // ReliefVM properties 
} 

und viele ähnliche Methoden:

[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); 
} 

Ich möchte so etwas bekommen:

[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); 
} 

Antwort

7

können Sie versuchen, Controller.UpdateModel oder Controller.TryUpdateModel Methode:

[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); 
} 

aber ich würde Ihnen vorschlagen, einen benutzerdefinierten ModelBinder zu erstellen, da er dafür zuständig ist, Modelleigenschaften zu erstellen und aufzufüllen.

kann ich Ihnen ein einfaches Beispiel zeigen, wie Sie dies erreichen können:

Basisansichtsmodell

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; 
    } 
} 

Danach das Modell Bindemittel in der Global.asax Register:

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

-Controller

[HttpPost] 
public ActionResult Index(StatementViewModel viewModel) 
{ 
    if (ModelState.IsValid) 
    { 
     // save the model 
    } 
    return View(viewModel); 
} 
0

Wenn ich Sie wäre, würde ich ein DTO (Data Transfer Object) verwenden, das den Namen der Ansicht und ein ViewModel umschließt, auf das über ein Interfac zugegriffen wird e. Sie haben dann so etwas wie:

[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); 
} 

Aber dies setzt voraus, dass Ihre Ansichten, die die Unterschiede zwischen den VMs behandeln ...

1

Sie zu verwenden Versuchten UpdateModel or TryUpdateModel, um Ihre Modellwerte aus der Formularsammlung zu initialisieren? Schauen Sie sich das folgende Codebeispiel

[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); 
} 
2

Sie können sich wahrscheinlich das Problem mit einem CustomModelBinder wie folgt lösen:

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); 
    } 
} 

Ihrer Aktion wird diese Signatur benötigt:

public ActionResult SaveStatement(User currentUser, StatementViewModel viewModel) 

aber die viewModel Sie Empfangen in der Methode wird vom entsprechenden abgeleiteten Typ sein, also sollten Sie in der Lage sein, es wie in den einzelnen Methoden zu tun.

Das einzige, was übrig bleibt, ist die benutzerdefinierte Sammelmappe im Global.asax zu registrieren.

Verwandte Themen