Freigeben über


Dieser Artikel wurde maschinell übersetzt.

ASP.NET MVC

Le caratteristiche e le debolezze di ASP.Associazione del modello MVC NET

Jess Chadwick

ASP.Associazione del modello NET MVC semplifica le azioni del controller con l'introduzione di un livello di astrazione che popola automaticamente i parametri di azione del controller, prendersi cura del mondano codice della proprietà mappatura e il tipo di conversione tipicamente coinvolto nel lavoro con ASP.Dati della richiesta di netto. Se l'associazione del modello sembra semplice, è in realtà un quadro relativamente complesso composti da un numero di parti che lavorano insieme per creare e popolare gli oggetti che richiedono la tua le azioni del controller.

In questo articolo vi porterà profondità nel cuore di ASP.NET MVC modello associazione sottosistema, mostrando ogni strato del quadro associazione modello e i vari modi è possibile estendere la logica di associazione modello per soddisfare le esigenze dell'applicazione. Lungo la strada, vedrete alcune tecniche di associazione del modello spesso trascurato e come evitare alcuni dei più comuni errori di associazione del modello.

Modello associazione Basics

Per capire quale associazione del modello è, prima prendere un'occhiata a un modo tipico per compilare un oggetto dalla richiesta di valori in una pagina ASP.Applicazione NET, mostrato in Figura 1.

Figura 1 recupero di valori direttamente dalla richiesta

public ActionResult Create()
{
  var product = new Product() {
    AvailabilityDate = DateTime.Parse(Request["availabilityDate"]),
    CategoryId = Int32.Parse(Request["categoryId"]),
    Description = Request["description"],
    Kind = (ProductKind)Enum.Parse(typeof(ProductKind), 
                                   Request["kind"]),
    Name = Request["name"],
    UnitPrice = Decimal.Parse(Request["unitPrice"]),
    UnitsInStock = Int32.Parse(Request["unitsInStock"]),
 };
 // ...
}

Quindi confrontare l'azione in Figura 1 con Figura 2, che sfrutta modello vincolante per produrre lo stesso risultato.

Figura 2 modello associazione a valori primitivi

public ActionResult Create(
  DateTime availabilityDate, int categoryId,
    string description, ProductKind kind, string name,
    decimal unitPrice, int unitsInStock
  )
{
  var product = new Product() {
    AvailabilityDate = availabilityDate,
    CategoryId = categoryId,
    Description = description,
    Kind = kind,
    Name = name,
    UnitPrice = unitPrice,
    UnitsInStock = unitsInStock,
 };
 
 // ...
}

Anche se i due esempi raggiungere la stessa cosa — un'istanza di prodotto popolata — il codice in Figura 2 si basa su ASP.NET MVC per convertire i valori dalla richiesta in valori fortemente tipizzati. Con associazione del modello, le azioni del controller possono essere focalizzate sulla fornitura di valore aziendale ed evitare di perdere tempo con richiesta mondano mapping e l'analisi.

Associazione a oggetti complessi

Anche se l'associazione del modello ai tipi primitivi, anche semplici può avere un impatto abbastanza grande, molte azioni di controllore si basano su più di un paio di parametri. Per fortuna, ASP.NET MVC gestisce tipi complessi bene come tipi primitivi.

Il codice seguente prende un altro passaggio all'azione crea, saltando i valori primitivi e l'associazione direttamente alla classe di prodotto:

public ActionResult Create(Product product)
{
  // ...
}

Ancora una volta, questo codice produce lo stesso risultato di azioni nel Figura 1 e Figura 2, solo che questa volta nessun codice è stato coinvolto a tutti — complessi ASP.Associazione del modello NET MVC eliminato tutto il codice boilerplate necessario per creare e popolare una nuova istanza di prodotto. Questo codice esemplifica il vero potere di associazione del modello.

Associazione del modello in decomposizione

Ora che avete visto in azione associazione del modello, è il momento di abbattere i pezzi che compongono il quadro di associazione del modello.

Associazione del modello è suddiviso in due fasi distinte: raccolta di valori dalla richiesta e la compilazione di modelli con tali valori. Queste operazioni vengono eseguite da fornitori di valore e modello leganti, rispettivamente.

Fornitori di valore

ASP.NET MVC include implementazioni del provider di valore che coprono le fonti più comuni di valori di richiesta come parametri querystring, campi modulo e dati di rotta. In fase di esecuzione, ASP.NET MVC utilizza il provider di valore registrati nella classe ValueProviderFactories per valutare i valori di richiesta che possono utilizzare i leganti modello.

Per impostazione predefinita, l'insieme di provider di valore valuta i valori da varie fonti nel seguente ordine:

  1. Precedentemente associata parametri di azione, quando l'azione è un'azione di bambino
  2. Campi modulo (Request. Form)
  3. I valori di proprietà nel corpo della richiesta di JSON (Request.InputStream), ma solo quando la richiesta è una richiesta AJAX
  4. Itinerario dei dati (RouteData.Values)
  5. Parametri QueryString (queryString)
  6. File inviati (Request.Files)

La collezione di provider di valore, come oggetto di richiesta, è davvero solo un dizionario glorificato — un livello di astrazione di coppie chiave/valore che modellano leganti può utilizzare senza bisogno di sapere dove i dati provengono da. Tuttavia, il quadro del fornitore di valore fa questa astrazione un passo oltre il dizionario richiesta, dandovi il controllo completo su come e dove il quadro di associazione modello ottiene i dati. È anche possibile creare provider di valore personalizzato.

Provider personalizzati di valore

Il requisito minimo per creare un provider personalizzato di valore è piuttosto semplice: Creare una nuova classe che implementa l'interfaccia System.Web.Mvc.ValueProviderFactory.

Ad esempio, Figura 3 viene illustrato un provider di valore personalizzato che recupera i valori dai cookie dell'utente.

Figura 3 Custom valore Provider Factory che ispeziona Cookie valori

public class CookieValueProviderFactory : ValueProviderFactory
{
  public override IValueProvider GetValueProvider
  (
    ControllerContext controllerContext
  )
  {
    var cookies = controllerContext.HttpContext.Request.Cookies;
 
    var cookieValues = new NameValueCollection();
    foreach (var key in cookies.AllKeys)
    {
      cookieValues.Add(key, cookies[key].Value);
    }
 
    return new NameValueCollectionValueProvider(
      cookieValues, CultureInfo.CurrentCulture);
  }
}

Si noti come il CookieValueProviderFactory è semplice. Invece di costruire un provider di valore nuovo di zecca da terra, il CookieValueProviderFactory semplicemente recupera i cookie dell'utente e sfrutta NameValueCollectionValueProvider per esporre tali valori per il quadro di associazione del modello.

Dopo aver creato un provider valore personalizzato, è necessario aggiungerlo all'elenco dei fornitori di valore tramite la raccolta di ValueProviderFactories.Factories:

var factory = new CookieValueProviderFactory();
ValueProviderFactories.Factories.Add(factory);

È abbastanza facile creare il provider personalizzato di valore, ma essere cauti nel farlo. L'insieme dei fornitori di valore che in ASP.NET MVC navi out of the box espone la maggior parte dei dati disponibili nell'HttpRequest (con l'eccezione di cookies, forse) abbastanza bene e in generale fornisce dati sufficienti per soddisfare la maggior parte degli scenari.

Per determinare se la creazione di un nuovo provider di valore è la cosa giusta da fare per lo scenario di particolare, la seguente domanda: L'insieme delle informazioni fornite dai provider esistenti valore contiene tutti i dati di cui che bisogno (anche se forse non in formato corretto)?

Se la risposta è no, quindi aggiungere un provider di valore personalizzato è probabilmente il modo giusto per affrontare il vuoto. Tuttavia, quando la risposta è sì — come è solito — considerare come è possibile compilare i pezzi mancanti personalizzando il funzionamento dell'associazione modello per accedere ai dati forniti dai fornitori di valore. Il resto di questo articolo mostra come di fare proprio questo.

Il componente principale di ASP.Quadro associazione NET MVC modello responsabile per la creazione e la compilazione di modelli che utilizzano i valori forniti da provider di valore è chiamato il raccoglitore di modello.

Raccoglitore di modello predefinito

L'ASP.NET MVC framework include implementazione di legante modello predefinita denominata DefaultModelBinder, che è stato progettato per associare in modo efficace la maggior parte dei tipi di modello. Lo fa utilizzando relativamente semplice e logica ricorsiva per ogni proprietà del modello di destinazione:

  1. Esaminare i provider di valore per vedere se la proprietà è stata scoperta come un tipo semplice o un tipo complesso controllando per vedere se il nome della proprietà è stato registrato come prefisso. I prefissi sono semplicemente HTML forma campo nome "puntino notazione" utilizzata per rappresentare se un valore è una proprietà di un oggetto complesso. Il modello di prefisso è [ParentProperty].[Proprietà]. Ad esempio, il campo del form con il nome UnitPrice.Amount contiene il valore per il campo importo della proprietà UnitPrice.
  2. Ottenere la ValueProviderResult da fornitori di valore registrato per il nome della proprietà.
  3. Se il valore è un tipo semplice, tenta di convertirlo nel tipo di destinazione. La logica di conversione predefinita si avvale TypeConverter della proprietà per convertire il valore di tipo stringa di origine nel tipo di destinazione.
  4. In caso contrario, la proprietà è un tipo complesso, quindi eseguire un'associazione ricorsiva.

Recursive modello di associazione

Associazione del modello ricorsiva efficacemente inizia il processo di associazione tutto il modello più volte, ma utilizza il nome della proprietà di destinazione come il nuovo prefisso. Utilizzando questo approccio, il DefaultModelBinder è in grado di attraversare l'intero complesso di oggetti grafici e popolare i valori delle proprietà anche profondamente nidificati.

Per vedere Associazione ricorsiva in azione, modificare Product.UnitPrice da un semplice tipo decimale al tipo personalizzato di valuta. Figura 4 mostra entrambe le classi.

Figure 4 classe prodotto con proprietà Unitprice complessa

public class Product
{
  public DateTime AvailabilityDate { get; set; }
  public int CategoryId { get; set; }
  public string Description { get; set; }
  public ProductKind Kind { get; set; }
  public string Name { get; set; }
  public Currency UnitPrice { get; set; }
  public int UnitsInStock { get; set; }
}
 
public class Currency
{
  public float Amount { get; set; }
  public string Code { get; set; }
}

Con questo aggiornamento sul posto, il raccoglitore di modello andrà a cercare i valori denominati UnitPrice.Amount e UnitPrice.Code per popolare la proprietà complessa Product.UnitPrice.

La logica di associazione DefaultModelBinder ricorsiva efficacemente possibile compilare anche i più complessi oggetti grafici. Finora, hai visto un oggetto complesso che risiedeva solo un profondo livello della gerarchia di oggetti, che il DefaultModelBinder gestite con facilità. Per dimostrare il vero potere di associazione del modello ricorsiva, aggiungere una nuova proprietà denominata bambino al prodotto con lo stesso tipo, il prodotto:

public class Product {
  public Product Child { get; set; }
  // ...
}

Quindi aggiungere un nuovo campo al form e — applicando la notazione con punto per indicare ogni livello — creare livelli come molti come si desidera. Di seguito è riportato un esempio di utilizzo di questo attributo.

    <input type="text" name="Child.Child.Child.Child.Child.Child.Name"/>

Questo campo modulo si tradurrà in sei livelli di prodotti! Per ogni livello, il DefaultModelBinder doverosamente verrà creata una nuova istanza di prodotto e destra tuffarsi vincolanti i suoi valori. Quando il gestore di associazione è tutto fatto, verrà creato un oggetto grafico che è simile al codice in Figura 5.

Figura 5 un oggetto grafico creato dall'associazione del modello Recursive

new Product {
  Child = new Product { 
    Child = new Product {
      Child = new Product {
        Child = new Product {
          Child = new Product {
            Child = new Product {
              Name = "MADNESS!"
            }
          }
        }
      }
    }
  }
}

Anche se questo esempio artificioso imposta il valore di solo una singola proprietà, esso è una grande dimostrazione su come la funzionalità di associazione DefaultModelBinder ricorsiva modello permette di sostenere qualche diritto di grafici oggetto molto complesso out of the box. Con l'associazione del modello ricorsiva, se è possibile creare un nome di campo modulo per rappresentare il valore per popolare, non importa dove nella gerarchia di oggetti che valorizza la vita — il raccoglitore di modello sarà trovare e associarlo.

Dove l'associazione del modello sembra caduta verso il basso

È vero: Ci sono alcuni modelli che il DefaultModelBinder semplicemente non sarà in grado di legare. Tuttavia, ci sono anche abbastanza alcuni scenari in cui la logica di associazione del modello predefinito non può sembrare a lavorare, ma in realtà funziona bene come usarlo in modo appropriato.

Di seguito sono alcuni degli scenari più comuni che gli sviluppatori spesso assumono che il DefaultModelBinder non può gestire e come si possono implementarle utilizzando il DefaultModelBinder e nient'altro.

Complessi raccolte ASP out-of-the-box.Fornitori di valore NET MVC trattano tutti i nomi dei campi di richiesta come se fossero i valori della forma post. Prendete, per esempio, un insieme di valori primitivi in un post di forma, in cui ogni valore richiede un proprio indice univoco (spazi bianchi aggiunto per migliorare la leggibilità):

MyCollection[0]=one &
MyCollection[1]=two &
MyCollection[2]=three

Lo stesso approccio può essere applicato anche alle collezioni di oggetti complessi. Per dimostrare questo, aggiornare la classe di prodotto per supportare più valute modificando la proprietà UnitPrice a un insieme di oggetti di valuta:

public class Product : IProduct
{
  public IEnumerable<Currency> UnitPrice { get; set; }
 
  // ...
}

Con questa modifica, sono necessari i seguenti parametri di richiesta di compilare la proprietà UnitPrice aggiornata:

UnitPrice[0].Code=USD &
UnitPrice[0].Amount=100.00 &

UnitPrice[1].Code=EUR &
UnitPrice[1].Amount=73.64

Prestare attenzione alla sintassi dei nomi dei parametri di richiesta necessari per associare le collezioni di oggetti complessi. Notate gli indicizzatori utilizzati per identificare ogni elemento unico nella zona e che ogni proprietà per ogni istanza deve contenere il pieno, indicizzate riferimento a tale istanza. Basta tenere a mente che il raccoglitore di modello si attende che i nomi delle proprietà di seguire l'invio di form sintassi, indipendentemente dal fatto che la richiesta sia un GET o un POST di denominazione.

Anche se è un po ' controintuitivo, JSON richieste hanno gli stessi requisiti — essi, troppo, deve rispettare il post di forma sintassi di denominazione. Prendiamo, per esempio, il payload JSON per la raccolta di UnitPrice precedente. La sintassi di matrice JSON pura per questi dati sarebbe stata rappresentata come:

[ 
  { "Code": "USD", "Amount": 100.00 },
  { "Code": "EUR", "Amount": 73.64 }
]

Tuttavia, i fornitori di valore predefinito e leganti modello richiedono i dati per essere rappresentato come un invio di form JSON:

{
  "UnitPrice[0].Code": "USD",
  "UnitPrice[0].Amount": 100.00,

  "UnitPrice[1].Code": "EUR",
  "UnitPrice[1].Amount": 73.64
}

Lo scenario di raccolta di oggetto complesso è forse uno degli scenari più ampiamente problematici che gli sviluppatori di incorrono in quanto la sintassi non è necessariamente evidente per tutti gli sviluppatori. Tuttavia, una volta che si impara la relativamente semplice sintassi per insiemi complessi di distacco, diventano molto più facili da trattare con questi scenari.

Leganti di modello generico Custom comunque il DefaultModelBinder è abbastanza potente per gestire qualsiasi cosa buttare a questo, ci sono volte che quando proprio non fa quello che serve. Quando si verificano questi scenari, molti sviluppatori salta al volo l'opportunità di trarre vantaggio del modello di estensibilità del framework associazione modello e costruire il proprio raccoglitore di modello personalizzato.

Ad esempio, anche se Microsoft.NET Framework fornisce supporto eccellente per principi orientato agli oggetti, il DefaultModelBinder non offre alcun supporto per l'associazione di astrarre le interfacce e le classi di base. Per dimostrare questa lacuna, la classe Product refactoring di modo che esso deriva da un'interfaccia — denominato IProduct — che consiste di proprietà di sola lettura. Allo stesso modo, aggiornare l'azione del controller Create in modo che accetta la nuova interfaccia IProduct anziché l'attuazione concreta del prodotto, come mostrato nella Figura 6.

Figura 6 vincolante su un'interfaccia

public interface IProduct
{
  DateTime AvailabilityDate { get; }
  int CategoryId { get; }
  string Description { get; }
  ProductKind Kind { get; }
  string Name { get; }
  decimal UnitPrice { get; }
  int UnitsInStock { get; }
}
 
public ActionResult Create(IProduct product)
{
  // ...
}

L'azione crea aggiornato mostrato in Figura 6— codice c# perfettamente legittimi, mentre — provoca DefaultModelBinder ad eccezione: "Non è possibile creare un'istanza di un'interfaccia". È comprensibile che il raccoglitore di modello genera questa eccezione, considerando che DefaultModelBinder ha alcun modo di sapere che tipo di calcestruzzo di IProduct per creare.

Il modo più semplice per risolvere il problema è quello di creare un raccoglitore di modello personalizzato che implementa l'interfaccia IModelBinder. Figura 7 mostra ProductModelBinder, un raccoglitore di modello personalizzato che sa come creare e associare un'istanza dell'interfaccia IProduct.

Figura 7 ProductModelBinder — un raccoglitore di modello personalizzato Tightly Coupled

public class ProductModelBinder : IModelBinder
{
  public object BindModel
    (
      ControllerContext controllerContext,
      ModelBindingContext bindingContext
    )
  {
    var product = new Product() {
      Description = GetValue(bindingContext, "Description"),
      Name = GetValue(bindingContext, "Name"),
  }; 
 
    string availabilityDateValue = 
      GetValue(bindingContext, "AvailabilityDate");

    if(availabilityDateValue != null)
    {
      DateTime date;
      if (DateTime.TryParse(availabilityDateValue, out date))
      product.AvailabilityDate = date;
    }
 
    string categoryIdValue = 
      GetValue(bindingContext, "CategoryId");

    if (categoryIdValue != null)
    {
      int categoryId;
      if (Int32.TryParse(categoryIdValue, out categoryId))
      product.CategoryId = categoryId;
    }
 
    // Repeat custom binding code for every property
    // ...
return product;
  }
 
  private string GetValue(
    ModelBindingContext bindingContext, string key)
  {
    var result = bindingContext.ValueProvider.GetValue(key);
    return (result == null) ?
null : result.AttemptedValue;
  }
}

L'aspetto negativo di leganti modello personalizzato che implementano l'interfaccia IModelBinder direttamente la creazione è che essi spesso duplicare molto di DefaultModelBinder solo per modificare alcune aree della logica. È anche comune per questi raccoglitori personalizzati di concentrarsi sulle classi modello specifico, creando un accoppiamento stretto tra il quadro e il livello di business e limitando il riutilizzo per supportare altri tipi di modello.

Per evitare di tutti questi problemi nel vostro leganti modello personalizzato, considerare derivanti da DefaultModelBinder e si esegue l'override di comportamenti specifici per soddisfare le vostre esigenze. Questo approccio spesso offre il meglio dei due mondi.

Astratto modello Binder l'unico problema con il tentativo di applicare associazione del modello a un'interfaccia con il DefaultModelBinder è che essa non sa come determinare il tipo di modello concreto. Prendere in considerazione l'obiettivo di livello superiore: la capacità di sviluppare le azioni del controller contro un tipo non-calcestruzzo e determinare in modo dinamico il tipo concreto per ogni richiesta.

Derivando da DefaultModelBinder e si esegue l'override solo la logica che determina il tipo di modello di destinazione, può non solo affrontare lo scenario IProduct specifico ma anche in realtà creano un raccoglitore di modello polivalente in grado di gestire la maggior parte delle altre gerarchie di interfaccia pure. Figura 8 mostra un esempio di un raccoglitore di modello astratto modello polivalente.

Figura 8 General-Purpose raccoglitore di modello astratto

public class AbstractModelBinder : DefaultModelBinder
{
  private readonly string _typeNameKey;

  public AbstractModelBinder(string typeNameKey = null)
  {
    _typeNameKey = typeNameKey ?? "
__type__";
  }

  public override object BindModel
  (
    ControllerContext controllerContext,
    ModelBindingContext bindingContext
  )
  {
    var providerResult =
    bindingContext.ValueProvider.GetValue(_typeNameKey);

    if (providerResult != null)
    {
      var modelTypeName = providerResult.AttemptedValue;

      var modelType =
        BuildManager.GetReferencedAssemblies()
          .Cast<Assembly>()
          .SelectMany(x => x.GetExportedTypes())
          .Where(type => !type.IsInterface)
          .Where(type => !type.IsAbstract)
          .Where(bindingContext.ModelType.IsAssignableFrom)
          .FirstOrDefault(type =>
            string.Equals(type.Name, modelTypeName,
              StringComparison.OrdinalIgnoreCase));

      if (modelType != null)
      {
        var metaData =
        ModelMetadataProviders.Current
        .GetMetadataForType(null, modelType);

        bindingContext.ModelMetadata = metaData;
      }
    }

    // Fall back to default model binding behavior
    return base.BindModel(controllerContext, bindingContext);
  }
}

Per sostenere l'associazione del modello a un'interfaccia, il raccoglitore di modello deve tradurre in primo luogo l'interfaccia in un tipo concreto. A tale scopo, AbstractModelBinder richiede la chiave "__type__" da fornitori di valore della richiesta. Sfruttando i fornitori di valore per questo tipo di dati fornisce la flessibilità per quanto cui è definito il valore "__type__". Ad esempio, la chiave potrebbe essere definita come parte del percorso (nei dati di rotta), specificata come parametro querystring o anche rappresentata come un campo di dati post del modulo.

Successivamente, il AbstractModelBinder utilizza il nome di tipo concreto per generare un nuovo set di metadati che descrivono i dettagli della classe concreta. AbstractModelBinder utilizza questo nuovo metadati per sostituire la proprietà ModelMetadata esistente che descritto il tipo di modello astratto iniziale, causando in modo efficace il raccoglitore di modello a dimenticare che esso è stato mai vincolato a un tipo di cemento non per cominciare.

Dopo AbstractModelBinder sostituisce il metadati di modello con tutte le informazioni necessarie per l'associazione al modello corretto, esso semplicemente rilascia controllo torna alla logica di base DefaultModelBinder per farla gestire il resto del lavoro.

Il AbstractModelBinder è un eccellente esempio che mostra come si può estendere la logica di associazione predefinita con la logica personalizzata senza reinventare la ruota, derivando direttamente dall'interfaccia IModelBinder.

Modello Binder selezione

Creazione di leganti modello personalizzato è solo il primo passo. Per applicare la logica di associazione modello personalizzato nell'applicazione, è necessario registrare anche i leganti modello personalizzato. La maggior parte dei tutorial mostrerà due modi per registrare leganti modello personalizzato.

L'insieme globale ModelBinders in genere per eseguire l'override di raccoglitore di modello per tipi specifici si consiglia di registrare un mapping di tipo a legante al dizionario ModelBinders.Binders.

Il seguente frammento di codice racconta il quadro utilizzare il AbstractModelBinder per associare modelli di valuta:

ModelBinders.Binders.Add(typeof(Currency), new AbstractModelBinder());

Si esegue l'override del gestore di associazione predefinito modello in alternativa, per sostituire il gestore globale predefinito, è possibile assegnare un raccoglitore di modello alla proprietà ModelBinders.Binders.DefaultBinder. Di seguito è riportato un esempio di utilizzo di questo attributo.

ModelBinders.Binders.DefaultBinder = new AbstractModelBinder();

Anche se questi due approcci funzionano bene per molti scenari, ci sono due modi più che in ASP.NET MVC permette di registrare un raccoglitore di modello per un tipo: gli attributi e i fornitori.

Che adornano i modelli con attributi personalizzati

Oltre ad aggiungere un mapping dei tipi al dizionario ModelBinders, ASP.NET MVC framework offre anche il System.Web.Mvc.CustomModelBinderAttribute astratta, un attributo che consente di creare in modo dinamico un raccoglitore di modello per ogni classe o proprietà a cui è applicato l'attributo. Figura 9 mostra un'implementazione di CustomModelBinderAttribute che crea un AbstractModelBinder.

Figura 9 CustomModelBinderAttribute attuazione

[AttributeUsage(
  AttributeTargets.Class | AttributeTargets.Enum |
  AttributeTargets.Interface | AttributeTargets.Parameter |
  AttributeTargets.Struct | AttributeTargets.Property,
  AllowMultiple = false, Inherited = false
)]
public class AbstractModelBinderAttribute : CustomModelBinderAttribute
{
  public override IModelBinder GetBinder()
  {
    return new AbstractModelBinder();
  }
}

È quindi possibile applicare la AbstractModelBinderAttribute a qualsiasi modello di classe o di proprietà, in questo modo:

public class Product
{
  [AbstractModelBinder]
  public IEnumerable<CurrencyRequest> UnitPrice { get; set; }
  // ...
}

Ora quando il raccoglitore di modello tenta di individuare il legante appropriato per Product.UnitPrice, scoprirete la AbstractModelBinderAttribute e utilizzare il AbstractModelBinder per associare la proprietà Product.UnitPrice.

Sfruttando gli attributi di raccoglitore di modello personalizzato è un ottimo modo per ottenere un approccio più dichiarativo alla configurazione leganti modello mantenendo la collezione di legante modello globale semplice. Inoltre, il fatto che gli attributi di raccoglitore di modello personalizzato possono essere applicati a entrambi intere classi e singole proprietà significa che hanno grana fine controllo sul processo di associazione modello.

Chiedi i leganti!

Modello legante provider offrono la possibilità di eseguire codice arbitrario in tempo reale per determinare il miglior binder possibile modello per un tipo specificato. Come tale, essi forniscono un eccellente via di mezzo tra registrazione legante modello esplicito per i tipi di modello individuale, registrazione basato su attributi statici e un raccoglitore di modello predefinito impostato per tutti i tipi.

Il codice seguente viene illustrato come creare un IModelBinderProvider che fornisce un AbstractModelBinder per tutte le interfacce e tipi astratti:

public class AbstractModelBinderProvider : IModelBinderProvider
{
  public IModelBinder GetBinder(Type modelType)
  {
    if (modelType.IsAbstract || modelType.IsInterface)
      return new AbstractModelBinder();
 
    return null;
  }
}

La logica di dettare se la AbstractModelBinder viene assegnato a un tipo di modello dato è relativamente semplice: È un tipo non-calcestruzzo? Se è così, il AbstractModelBinder è il legante modello appropriato per il tipo, quindi crea un'istanza di raccoglitore di modello e restituirlo. Se il tipo è un tipo concreto, allora AbstractModelBinder non è appropriato; restituire un valore null per indicare che il raccoglitore di modello non è una partita di questo tipo.

Una cosa importante da tenere a mente quando si implementa la.GetBinder logica è che la logica verrà eseguita per ogni proprietà che è un candidato per l'associazione del modello, quindi assicuratevi di mantenerlo leggero o si possono facilmente introdurre problemi di prestazioni nelle applicazioni.

Al fine di iniziare a utilizzare un provider del modello legante, aggiungerlo all'elenco dei fornitori mantenuto nell'insieme ModelBinderProviders.BinderProviders. Ad esempio, il AbstractModelBinder del Registro di sistema come segue:

var provider = new AbstractModelBinderProvider();
ModelBinderProviders.BinderProviders.Add(provider);

E che facilmente, che hai aggiunto modello supporto dell'associazione per i tipi non-calcestruzzo in tutta l'intera applicazione.

L'approccio di associazione modello rende modello associazione selezione molto più dinamico prendendo l'onere di determinare il raccoglitore di modello corretto dal quadro e mettendolo nel luogo più adatto: i leganti modello se stessi.

Punti di estendibilità chiave

Come qualsiasi altro metodo, ASP.Associazione del modello MVC NET permette le azioni del controller accettare tipi di oggetti complessi come parametri. Associazione del modello favorisce anche la migliore separazione dei problemi separando la logica del popolamento di oggetti dalla logica che utilizza gli oggetti popolati.

Ho ho esplorato alcuni punti di estendibilità chiave nel quadro associazione modello che possono aiutarvi a sfruttare al meglio. Prendendo il tempo di capire ASP.Associazione del modello NET MVC e come usarlo correttamente può avere un grande impatto, anche il più semplice delle applicazioni.

Jess Chadwick è un consulente software indipendente specializzata in tecnologie Web. Egli ha più di un decennio di esperienza di sviluppo che vanno da dispositivi embedded in Start-up a farm Web su scala aziendale presso aziende Fortune 500. Egli è un ASPInsider, Microsoft MVP in ASP.NET e libro e autore di rivista. Egli è attivamente coinvolto nella comunità di sviluppo, regolarmente a conferenze e gruppi di utenti così come portando il NJDOTNET centrale New Jersey.NET user group.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Phil Haack