2009-07-03 5 views
2
in den Parameter

Lassen Sie uns sagen, ich habe eine Schnittstelle wie:ASP.NET MVC: enge Kopplung vermeiden, wenn das Formular POST Bindung

interface IThing { 
    int Id { get; set; } 
    string Title { get; set; } 
} 

Und in ASP.NET MVC habe ich eine Form, die Beiträge zu einer Controller-Aktion wie diese :

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult NewThing([Bind(Exclude = "Id")] SimpleThing thing) { 

    // code to validate and persist the thing can go here    
} 

Wo SimpleThing eine konkrete Klasse, die gerade noch implementiert IThing.

Allerdings möchte ich alle meine Methoden mit der Schnittstelle beschäftigen. Ich habe eine Datenbaugruppe, die NHiberate und seine eigene IThing Implementierung (nennen wir es RealThing) verwendet. Ich kann das SimpleThing nicht weitergeben, weil es sich über eine "unbekannte Entität" beschweren wird.

Hat jemand irgendwelche Ideen über einen saubereren Weg, dies zu tun? Ich habe über etwas nachgedacht, das einer Fabrikklasse entspricht. Aber wie würde ich den MVC-Formularbinder dazu bringen, ihn zu benutzen?

Danke!

Antwort

1

Es gab einige gute Vorschläge hier und ich kam tatsächlich mit einer Lösung, die funktioniert. Am Ende habe ich etwas gefunden. Ich habe gerade Modelle speziell für die Formulardaten erstellt, die ich gepostet habe, und die Standardmodellmappe verwendet.

Viel einfacher und es ermöglicht mir, Daten zu erfassen, die nicht Teil meines Domänenmodells ist (dh wie ein "Kommentar" -Feld).

+0

Hallo user10789, hast du dieses schnelle Kopfgeld gesetzt? –

4

Sie können benutzerdefinierte model binders verwenden. Der Artikel auf msdn ist jedoch völlig nutzlos. Also besser, Suche zu betreiben und etwas Besseres zu finden. Artikel sind verfügbar.

3

Ich kam auf zwei Ansätze dazu.

Die erste war Code zu meiner NHibernate Repository-Klasse hinzufügen, um den einfachen POCO-Typen von dem MVC-Controller verwendet, zu übersetzen, um die Art der Einheit ( SimpleThing), die NHibernate (RealThing) wollte:

/// <summary> 
/// A NHibernate generic repository. Provides base of common 
/// methods to retrieve and update data. 
/// </summary> 
/// <typeparam name="T">The base type to expose 
/// repository methods for.</typeparam> 
/// <typeparam name="K">The concrete type used by NHibernate</typeparam> 
public class NHRepositoryBase<T, K> 
    : IRepository<T> 
    where T : class 
    where K : T, new() 
{ 
    // repository methods ... 

    /// <summary> 
    /// Return T item as a type of K, converting it if necessary 
    /// </summary>   
    protected static K GetKnownEntity(T item) { 
     if (typeof(T) != typeof(K)) { 
      K knownEntity = new K(); 

      foreach (var prop in typeof(T).GetProperties()) { 
       object value = prop.GetValue(item, null); 
       prop.SetValue(knownEntity, value, null); 
      } 

      return knownEntity; 
     } else { 

      return (K)item; 
     }    
    } 

So kann jede Methode im Repository GetKnownEntity (T-Element) aufrufen und die Eigenschaften des übergebenen Elements in den Typ kopieren, den NHibernate möchte. Offensichtlich fühlte sich das ein wenig klobig an, also schaute ich mich nach maßgefertigten Modellbindern um.


Im zweiten Ansatz, habe ich ein benutzerdefiniertes Modell Bindemittel wie folgt aus:

public class FactoryModelBinder<T> 
    : DefaultModelBinder 
    where T : new() 
{ 

    protected override object CreateModel(ControllerContext controllerContext, 
              ModelBindingContext bindingContext, 
              Type modelType) { 

     return new T();      
    } 

} 

Dann habe ich registriert, dass in Global.asax.cs mit:

ModelBinders.Binders.Add(typeof(IThing), 
      new FactoryModelBinder<RealThing>()); 

Und es funktioniert gut mit einer Controller-Aktion, die wie folgt aussieht:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult NewThing([Bind(Exclude = "Id")] IThing thing) { 
    // code to process the thing goes here 
} 

Ich mag den zweiten Ansatz, aber die meisten meiner Abhängigkeitsinjektion Zeug ist in der Controller-Klasse. Ich möchte nicht alle diese ModelBinder-Zuordnungen in Global.asax.cs hinzufügen müssen.

+0

Ich denke, zweite Lösung ist sehr einfach zu automatisieren (entweder mit Code-Generierung oder mit Reflexion). –

0

Dies ist nicht direkt auf Ihre Frage unswer.

Wir verwenden einen etwas anderen Ansatz, um mit dem gleichen Problem umzugehen, das Sie haben. Unsere Controller akzeptieren DTOs, die Feld für Feld mit persistenter Entität übereinstimmen.Dann verwenden wir den Benutzer AutoMapper, um Persisten-Entitäten zu erstellen, die zur Datenbank gehen. Dies beseitigt unnötige Schnittstellen und sperrt öffentlich zugängliche API (bedeutet, dass das Umbenennen des Objekts des Persistenzobjekts unseren Client-Code nicht verletzt).