2013-05-25 9 views
16

Ich versuche, ein benutzerdefiniertes Modellbinder für MVC 4 zu erstellen, das von DefaultModelBinder erben wird. Ich möchte, dass es Schnittstellen unter Binding-Ebene abfangen und versuchen, den gewünschten Typ aus einem versteckten Feld namens AssemblyQualifiedName laden.Benutzerdefiniertes Modellbinder, der von DefaultModelBinder erbt

Hier ist, was ich bisher haben (vereinfacht):

public class MyWebApplication : System.Web.HttpApplication 
{ 
    protected void Application_Start() 
    { 
     ModelBinders.Binders.DefaultBinder = new InterfaceModelBinder(); 
    } 
} 

public class InterfaceModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, 
     ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType.IsInterface 
      && controllerContext.RequestContext.HttpContext.Request.Form.AllKeys.Contains("AssemblyQualifiedName")) 
     { 
      ModelBindingContext context = new ModelBindingContext(bindingContext); 

      var item = Activator.CreateInstance(
       Type.GetType(controllerContext.RequestContext.HttpContext.Request.Form["AssemblyQualifiedName"])); 

      Func<object> modelAccessor =() => item; 
      context.ModelMetadata = new ModelMetadata(new DataAnnotationsModelMetadataProvider(), 
       bindingContext.ModelMetadata.ContainerType, modelAccessor, item.GetType(), bindingContext.ModelName); 

      return base.BindModel(controllerContext, context); 
     } 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 

Beispiel Create.cshtml Datei (vereinfacht):

@model Models.ScheduledJob 

@* Begin Form *@ 
@Html.Hidden("AssemblyQualifiedName", Model.Job.GetType().AssemblyQualifiedName) 

@Html.Partial("_JobParameters") 
@* End Form *@ 

Die obige Teil _JobParameters.cshtml schaut auf die Model.Job ‚s Eigenschaften und baut die Bearbeitungssteuerelemente, ähnlich wie @Html.EditorFor(), aber mit etwas extra Markup. Die ScheduledJob.Job Eigenschaft ist vom Typ IJob (Schnittstelle).

Beispiel ScheduledJobsController.cs (vereinfacht):

[HttpPost] 
public ActionResult Create(ScheduledJob scheduledJob) 
{ 
    //scheduledJob.Job here is not null, but has only default values 
} 

Wenn ich die Form speichern, interpretiert er den Objekttyp korrekt und bekommt eine neue Instanz, aber die Eigenschaften des Objekts sind nicht auf ihre entsprechenden eingestellt wird Werte.

Was muss ich sonst tun, um dem Standardordner mitzuteilen, dass er die Eigenschaftsbindung des angegebenen Typs übernehmen soll?

Antwort

21

This article zeigte mir, dass ich das Modell Binder zu kompliziert war. Der folgende Code funktioniert:

public class InterfaceModelBinder : DefaultModelBinder 
{ 
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     if (bindingContext.ModelType.IsInterface) 
     { 
      Type desiredType = Type.GetType(
       EncryptionService.Decrypt(
        (string)bindingContext.ValueProvider.GetValue("AssemblyQualifiedName").ConvertTo(typeof(string)))); 
      bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, desiredType); 
     } 

     return base.BindModel(controllerContext, bindingContext); 
    } 
} 
+0

Wo haben Sie den obigen Binder hinzugefügt? Ich habe Binder ModelBinders.Binders.Add (typeof (Organization), neuer OrganizationModelBinder (DependencyResolver.Current.GetService ())); auf der Seite global.asax.cs in der App_start() -Methode. Ich bin nur auf der Suche nach besseren Möglichkeiten, um die Modellbinder zu registrieren, wie wir routen und bündeln? – mmssaann

+0

@mmssaann Ich habe 'ModelBinders.Binders.DefaultBinder = new MyCustomModelBinder(); 'in global.asax.cs' Application_Start() 'verwendet. Dann habe ich meinen ModelBinder generisch genug gemacht, um mit jeder abstrakten Klasse oder Schnittstelle umgehen zu können, ansonsten überlasse ich die Bindungslogik dem DefaultModelBinder. –

1

Mit MVC 4 ist es leicht, die Nachrichten außer Kraft zu setzen, wenn das alles ist, dass Sie in einem benutzerdefinierten Modell Binder benötigen:

protected void Application_Start(object sender, EventArgs e) 
    { 
     //set mvc default messages, or language specifc 
     ClientDataTypeModelValidatorProvider.ResourceClassKey = "ValidationMessages"; 
     DefaultModelBinder.ResourceClassKey = "ValidationMessages"; 
    } 

Dann ValidationMessages benannte Ressource-Datei erstellen mit Einträge wie dieses:

NAME: FieldMustBeDate 
VALUE: The field {0} must be a date. 
NAME: FieldMustBeNumeric 
VALUE: The field {0} must be a number 

.

Wir taten dies für einen Compliance-Fehler. Unser Sicherheitsscan hat nicht gefallen, dass eine javascript Injektion zurückkommt und in den Validierungsnachrichten erscheint und ausgeführt wird. Durch Verwendung dieser Implementierung überschreiben wir die Standardnachrichten, die den vom Benutzer bereitgestellten Wert zurückgeben.

Verwandte Themen