2012-10-29 7 views
8

Ho una cosa abbastanza complessa inserita in un modello T4. Fondamentalmente io prendo qualcosa comeC'è qualcosa là fuori per rendere il codice T4 più ... pulito?

{= foo =} più testo ...

e convertirlo in una classe (vista) in questo modo:

public class MyView 
{ 
    public string foo{get;set;} 
    public string Write() 
    { 
    return [email protected]" more text..."; 
    } 
} 

Il codice generato è ovviamente molto più complicato di così. Ad ogni modo, il modello T4 ha oltre 600 linee di codice in questo momento e sta diventando davvero veloce e ingestibile. Credo che il problema principale sia la combinazione di codice e "contenuto" (cioè codice statico). Non sono sicuro di come risolvere questo problema in modo pulito (anche se ovviamente non influisce sul codice generato). Inoltre, non vedo alcun modo fattibile per testare il mio codice T4, oltre a provare semplicemente gli errori di esecuzione T4. E ovviamente sembra esserci il compito quasi impossibile di testare il codice generato.

Ci sono "Model-View" tipo quadri o tecniche che posso usare per fare il mio codice del template T4 più pulito?

+0

Questa opzione potrebbe non essere un'opzione, ma preferisco [Modelli Resharper] (http://www.jetbrains.com/resharper/features/code_templates.html) su modelli T4. Inoltre, [Resharper] (http://www.jetbrains.com/resharper/) è uno strumento straordinario anche per altri usi. Ne vale la pena. – TylerOhlsen

+2

Resharper è un buon strumento, ma IMHO che confronta i modelli di Resharper con T4 sta confrontando mele e arance. I modelli di Resharper stanno promuovendo l'anti-pattern di copia-incolla con il supporto degli strumenti che porta a un codice sempre più ridondante che devi mantenere, aumentando così i costi di manutenzione. T4 (e altri strumenti) minimizza la ridondanza in quanto si scrive un meta-programma (che ha una ridondanza bassa) che genera artefatti di codice (con elevata quantità di ridondanza). La chiave è che la connessione casuale tra il meta-programma e il codice generato non viene persa. – FuleSnabel

risposta

1

Dopo un lungo viaggio, ho finalmente controllato i primi test di unità sui miei modelli T4. Fondamentalmente, ciò che ho fatto è stato astrarre una "vista" (il modello T4 effettivo) e la "logica" (ciò che genera il codice, ma in realtà non lo emette e non si basa su T4)

I poi ha fatto un ulteriore passo avanti e ha usato un grande hack per far sì che il mio file logico compaia fuori da T4. Questo ha avuto il buon effetto di far sì che gli errori intellisense e del compilatore funzionassero. Mi consente inoltre di accedere alla mia classe logica dai test unitari semplicemente facendo riferimento al progetto.

Ho scritto un articolo completo di esempi di codice (prima/dopo) e test di unità di esempio on my blog, se si desiderano i dettagli lunghi su come farlo.

5

IMHO i due concetti più importanti durante la scrittura di modelli complessi è

  1. Separare il modello e la vista - Questo aiuta a mantenere la logica del modello al minimo (spesso causa di problemi di manutenzione). Personalmente non penso che ciò richieda un quadro, richiede solo che tu lo faccia.
  2. parziale è tuo amico - io uso T4, in generale, per generare il codice di scheletro dal modello. Un comportamento specifico potrebbe non valere la pena di inserire il modello, meglio spesso per consentire che tale comportamento entri attraverso l'uso di classi o metodi parziali.

Non è così importante, ma bello

  1. rendere il codice ricercabile - Non mi baso su T4 Addons perché trovo nessuno di loro abbastanza buono, con la mancanza di IntelliSense per navigare il codice che devo rendere il codice ricercabile. Può essere semplice come invece di chiamare un nome di proprietà Column, lo chiamo ColumnName.
  2. Inserire "tag" nell'output - Rende più semplice trovare il codice che ha generato quella parte dell'output.

Esempio separa modello/Vista:

<# 
    // Model for Dependency Pooperties 
    Model = new [] 
    { 
     new ClassDefinition ("MyDataGrid") 
      { 
       P ("CultureInfo"   , "CultureInfo"), 
       P ("Pen"     , "CellBorderPen"), 
       P ("IEnumerable<object>" , "Rows"), 
       C ("WColumnDefinition"  , "Columns"), 
      }, 
    }; 
#> 
// Include the view 
<#@ include file="..\T4\DependencyProperties.ttinclude" #> 

La vista poi itera il modello e generare le proprietà di dipendenza.

Il comportamento delle proprietà di dipendenza vengono quindi implementata come metodi parziali

partial void Changed_Columns(
     ObservableCollection<WColumnDefinition> oldValue, 
     ObservableCollection<WColumnDefinition> newValue 
     ) 
    { 
     HookUpColumns(oldValue, null); 
     HookUpColumns(newValue, this);    
    } 

noti che mettere questo comportamento specifico nel modello complicherebbe notevolmente il modello.

Infine

; ci vuole tempo anche per un programmatore competente a scrivere metaprogrammi in modo competente.Mi ci sono voluti diversi tentativi prima di arrivare a uno stile che ritengo sia mantenibile, ma per me è valsa la pena, dato che sono in grado di spedire la qualità più velocemente.

Spero che questo aiuti ...

PS. Non penso che nessuno possa sostenere che T4 sia sempre elegante ma è maledettamente utile.

+0

In realtà ho considerato di separare il "modello" da un normale assemblaggio e di avere una semplicistica "vista" T4 che gestisce effettivamente la generazione del codice e quindi il collegamento del codice T4 all'assembly regolare. Questo risolve anche la maggior parte di le complicazioni del test unitario – Earlz

+0

Ok, penso di aver trovato un modo "buono" per fare almeno un test unitario, che costringe anche a una rigorosa separazione tra "vista" e "modello". Pubblicherò qui quando otterrò tutti i dettagli ordinati, ma fondamentalmente implica avere il "modello" in un diverso file '.cs' che viene incluso nell'output T4 generato. Non è perfetto, ma è il migliore che ho trovato finora – Earlz

+1

Si potrebbe voler guardare la mia [auto-risposta] (http://stackoverflow.com/a/13486081/69742) per come ho finito per fare questo, fare intellisense, errori del compilatore e unit test funzionano sulla logica usata dal mio modello T4 – Earlz

Problemi correlati