Il presente articolo è stato tradotto automaticamente.

Microsoft .NET Framework

Migrazione di librerie .NET legacy a piattaforme moderne di destinazione

Josh Lane

Uno dei punti di forza thegreatest del Microsoft .NET Framework è l'ampia varietà di terze parti open source e commerciali librerie che la piattaforma di destinazione. È un testamento alla maturità dell'ecosistema di sviluppo .NET che avete non solo grande API da scegliere all'interno del Framework .NET stesso, ma anche migliaia di librerie non Microsoft per servire le richieste HTTP, disegno griglie in un'applicazione desktop, memorizzazione dati strutturati su file system e tutto nel fratempo. Infatti, una lettura veloce dei repository di codice .NET popolare dimostra più di 32.000 progetti su CodePlex, più di 5.000 esempi di codice su Microsoft.com e più di 10.000 pacchetti unici sulla Galleria NuGet!

L'emergere di nuove piattaforme di software come Windows Phone 8 e Windows 8 ha il potenziale per infondere nuova vita in questi tried-and-true codebase. .NET librerie che hanno servito bene per anni sul desktop e server possono rivelarsi altrettanto (e a volte più) utile in questi nuovi ambienti — condizione siete disposti a spendere lo sforzo di migrazione necessario per queste nuove piattaforme di destinazione. Tradizionalmente tale attività può essere difficile e noioso, ma mentre cura e pianificazione esplicita sono ancora necessari per garantire il successo, Visual Studio 2012 ha diverse caratteristiche che minimizzare potenziali difficoltà e massimizzare le opportunità per il riutilizzo attraverso le piattaforme.

In questo articolo, esplorerò le sfide trovate durante una migrazione in avanti reali dell'oggetto Sterling NoSQL open source -­progetto di database orientato (OODB). Io a piedi attraverso una breve panoramica della libreria, condividere il migrazione ostacoli incontrati e le soluzioni per superarli e poi avvolgere considerando alcuni modelli e la migliore consulenza pratiche è possibile sfruttare nel proprio impegno di migrazione libreria.

Che cosa è Sterling?

Sterling è una leggera NoSQL stoccaggio raccolta di dati che fornisce il recupero veloce, indicizzata di istanze della classe .NET. Sono supportati anche gli aggiornamenti, eliminazioni, backup e ripristino, troncamento e simili, però come altre tecnologie NoSQL non fornisca un generale -­scopo, struttura query basato sul linguaggio SQL. Invece, la nozione di "query" consiste in una serie di operazioni distinte, ha ordinate:

  • In primo luogo, di recuperare un insieme di tasti predefinite o indici mappati a istanze di classe lazy-caricato.
  • Indice successivo, in chiave insieme a iniziale veloce filtraggio del set di risultati possibili intera.
  • Infine, utilizzare standard LINQ query to oggetti contro le coppie chiave-valore ora-filtrati per perfezionare ulteriormente i risultati.

Chiaramente il modello di utilizzo per Sterling (e per i database NoSQL simili) differisce da quella offerta dai tradizionali database relazionali come SQL Server. La mancanza di un formale, linguaggio query distinct sembra particolarmente strano per i nuovi arrivati. In realtà, questo è presentato dai fautori di NoSQL come una forza, data la potenziale complessità e overhead associato alla mappatura di ingressi e uscite tra il mondo della query e del mondo del codice. In una soluzione NoSQL come Sterling, non non c'è alcuna mappatura perché sono la stessa query e codice.

Un trattamento completo di riposti di Sterling è oltre la portata di questo articolo (vedi Jeremy Likness' articolo, "Sterling per isolato archiviazione su Windows Phone 7," a msdn.microsoft.com/magazine/hh205658 per ulteriori dettagli), ma potrai evidenziare alcuni vantaggi chiave e compromessi da tenere a mente:

  • Ha dimensioni ridotte (circa 150K su disco) ed è adatto per ospitare nel processo.
  • Funziona out-of-the-box con la gamma standard dei tipi serializzabili .NET.
  • L'insieme dei concetti necessari per le funzionalità di base è piccola; può essere installato e funzionante con Sterling in appena cinque linee di codice c#.
  • Funzionalità di database tradizionali come protezione granulare, aggiornamenti a cascata e le eliminazioni, configurabile semantica di blocco, atomicità, consistenza, isolamento, garantisce la durata (acido), e così via non sono supportati in sterline. Se avete bisogno di queste caratteristiche, si dovrebbe considerare un motore completo relazionale come SQL Server.

Il creatore di Sterling (mio collega di Wintellect, Jeremy Likness) destinato fin dall'inizio che target più piattaforme; ha creato i binari per il .NET Framework 4, Silverlight 4 e 5 e Windows Phone 7. Così quando si considera il lavoro necessario per aggiornare Sterling per indirizzare il .NET Framework 4.5, Windows Phone 8 e Windows Store apps, sapevo che l'architettura sarebbe si presta ad un tale sforzo, ma non sapevo esattamente che cosa il progetto comporterebbe.

Il Consiglio che delineare in questo articolo è il risultato diretto della mia esperienza aggiornamento Sterling per indirizzare il .NET Framework 4.5, Windows Phone 8 e Windows Store apps. Mentre alcuni dettagli del mio viaggio sono specifiche per il progetto di Sterling, molte altre sono pertinenti ad una vasta gamma di progetti e sforzi porting nell'ecosistema Microsoft.

Sfide e soluzioni

Riflettendo sugli ostacoli ho affrontato come porting di Sterling per le nuove piattaforme di destinazione, poche grandi categorie emersero sotto che sono in grado sia grumo i problemi e fornire indicazioni più generali per chiunque intraprendere questo tipo di progetto.

Ospitare divergenti filosofie di Design la prima serie di potenziali problemi è un po ' filosofica in natura, anche se ha un impatto reale sull'impegno globale di migrazione potrai spendere. Chiedete a voi stessi: "In quale misura l'architettura e il design della libreria che voglio migrare allineare con i modelli comuni e modelli di utilizzo delle nuove piattaforme di destinazione?"

Questa non è una domanda facile da risolvere, e pulite, taglia unica risposte sono sfuggente. Il vostro gestore di layout di Windows Form intelligente, personalizzato potrebbe essere difficile o impossibile a porta a Windows Presentation Foundation (WPF). Le API sono certamente distinte, ma è il nucleo molto differenti design filosofie e nozioni di controllo di gestione e il posizionamento di questi due mondi che sono suscettibili di viaggio ti fino a lungo termine. Come altro esempio, personalizzati controlli di input dell'interfaccia utente che funzionano bene per il classico stile di input di tastiera e mouse potrebbero produrre un povero UX per ambienti basati su touch come Windows Phone o Windows 8. Il semplice desiderio di migrare un codebase avanti non è sufficiente; ci deve essere una compatibilità di disegno sottostante tra vecchie e nuove piattaforme, più la volontà di riconciliare qualunque lievi differenze che esistono. Nel caso di Sterling, ho avuto pochi tali dilemmi di lavorare attraverso.

Il più importante problema di progettazione è stata la mancata corrispondenza tra il sincrono Sterling dati aggiornamento API e della natura asincrona prevista di tale comportamento in librerie target Windows Phone 8 e Windows Store apps che. Sterling è stato progettato molti anni fa in un mondo dove le API asincrone erano rare, e gli strumenti e le tecniche per la creazione di loro erano grezzi al meglio.

Ecco un tipico Salva firma di metodo in sterline prima della migrazione:

object Save<T>(T instance) where T : class, new()

Che cosa è importante da notare qui è che questo metodo viene eseguito in modo sincrono; che è, non importa quanto tempo ci vuole per salvare l'argomento dell'istanza, il chiamante è bloccato, in attesa per il metodo completare. Ciò può causare la solita gamma di problemi di blocco dei thread: interfacce utente non risponde, ridotto drammaticamente la scalabilità del server e così via.

Le aspettative di utenti per quanto riguarda la progettazione di software reattivo sono aumentati drammaticamente negli ultimi anni diversi; nessuno di noi vuole mettere in su con un'interfaccia utente bloccata per diversi secondi durante l'attesa di un salvataggio al completamento dell'operazione. In risposta, linee guida di progettazione di API per nuove piattaforme, come Windows Phone 8 e Windows 8 mandato che Metodi public library come salvare essere operazioni asincrone, non-blocking. Per fortuna, caratteristiche come il .NET Task-based Asynchronous Pattern (TAP) modello di programmazione c# async e attendono Parole chiavi fare questo ora più facile. Ecco la firma aggiornata per salvare:

Task<object> SaveAsync<T>(T instance) where T : class, new()

Ora salvare restituisce immediatamente, e il chiamante ha un oggetto awaitable (Task) da utilizzare per la eventuale raccolta del risultato (in questo caso, la chiave univoca dell'istanza appena salvata). Il chiamante non viene bloccato da fare altri lavori durante il salvataggio operazione viene completata in background.

Per essere chiari, tutto quello che vi ho mostrato qui sono le firme del metodo; la conversione da sincrono all'implementazione asincrona sotto il cofano richieste aggiuntive di refactoring e uno switch per l'API di file asincrono per ogni piattaforma di destinazione. Per esempio, l'implementazione sincrona di salvataggio utilizzato un BinaryWriter per scrivere il file system:

using ( BinaryWriter instanceFile = _fileHelper.GetWriter( instancePath ) )
{
  instanceFile.Write( bytes );
}

Ma perché BinaryWriter non supporta la semantica di async, io ho rifattorizzato per utilizzare le API asincrone appropriate per ogni piattaforma di destinazione. Ad esempio, Figura 1 illustrato l'aspetto di SaveAsync per il driver di archiviazione Windows Azure tabella Sterling.

Figura 1 come SaveAsync cerca il Driver di archiviazione di Sterling Windows Azure tabella

using ( var stream = new MemoryStream() )
{
  using ( var writer = new BinaryWriter( stream ) )
  {
    action( writer );
  }
  stream.Position = 0;
  var entity = new DynamicTableEntity( partitionKey, rowKey )
  {
    Properties = new Dictionary<string, EntityProperty>
    {
      { "blob", new EntityProperty( stream.GetBuffer() ) }
    }
  };
  var operation = TableOperation.InsertOrReplace( entity );
  await Task<TableResult>.Factory.FromAsync(
    table.BeginExecute, table.EndExecute, operation, null );
}

Io uso ancora BinaryWriter per scrivere valori discreti in un flusso in memoria, ma poi utilizzare il Windows Azure DynamicTableEntity e CloudTable.BeginExecute e.EndExecute per memorizzare in modo asincrono byte di matrice contenuto del flusso nel servizio di archiviazione Windows Azure tabella. C'erano parecchi altri, simili cambiamenti necessari per ottenere il comportamento di aggiornamento dei dati asincrona di Sterling. Il punto chiave: refactoring di superficie-livello API può essere solo il primo di diversi passaggi necessari per raggiungere un obiettivo di riprogettazione di migrazione come questo. Pianificare le attività di lavoro e sforzo stime di conseguenza ed essere realistici sul fatto che un tale cambiamento è anche un obiettivo ragionevole in primo luogo.

In realtà, la mia esperienza con Sterling emerse solo come un obiettivo irrealistico. Un core design caratteristico di Sterling è che tutte le operazioni di archiviazione lavoro contro dati fortemente tipizzati, utilizzando standard .NET contratto di serializzazione dei dati API ed estensioni. Questo funziona bene per i client Windows Phone e .NET 4.5, così come C#-based apps Store di Windows. Tuttavia, non non c'è nessuna nozione della tipizzazione forte nel mondo dei client Windows Store HTML5 e JavaScript. Dopo qualche ricerca e di discussione con Likness, decisi che non c'era no easy way to a disposizione Sterling questi client, così ho scelto di ometterli opzioni come supportate. Tali disallineamenti potenziali devono naturalmente essere considerate caso per caso, ma sanno che possono sorgere ed essere realistici circa le vostre opzioni.

Condividere codice attraverso piattaforme di destinazione la prossima grande sfida che ho affrontato è stato uno che abbiamo incontrato in una volta o l'altra: come condividere codice comune tra più progetti?

Identificazione e condivisione di codice comune tra progetti è una collaudata strategia per minimizzare time-to-market e mal di testa a valle della manutenzione. Abbiamo fatto questo per anni in .NET; un tipico pattern consiste nel definire un'Assemblea comune e che di riferimento da più progetti rivolti ai consumatori. Un'altra tecnica preferita è la funzionalità di "Aggiungere come Link" in Visual Studio, che concede la possibilità di condividere un singolo master file sorgente tra più progetti, dimostrato Figura 2.

The Visual Studio 2012 Add As Link Feature
Figura 2 la Visual Studio 2012 Inserisci funzione Link

Ancora oggi, queste opzioni funzionano bene se i progetti rivolti ai consumatori tutti i target della stessa piattaforma sottostante. Tuttavia, quando si desidera esporre funzionalità comuni su più piattaforme (come nel mio caso con Sterling), creando un unico assembly comune per tale codice diventa un onere significativo sviluppo. Creazione e manutenzione di costruire bersagli multipli diventa una necessità, che aumenta la complessità del processo di configurazione e compilazione del progetto. Uso delle direttive per il preprocessore (#if, #endif e così via) per includere condizionalmente comportamento specifico della piattaforma per determinate configurazioni della build è una necessità virtuale, che rende il codice più difficile da leggere, navigare e ragionare sul. Energia sprecata su tali oneri di configurazione distrae dall'obiettivo primario di risolvere problemi reali tramite codice.

Felicemente, Microsoft prevede la necessità di uno sviluppo multi-piattaforma più semplice e, all'inizio del .NET Framework 4, aggiunto una nuova funzionalità denominata portatile Class Libraries (tributo). Tributo consentono di indirizzare in modo selettivo più versioni di .NET Framework, Silverlight e Windows Phone, così come archivio di Windows e Xbox 360, tutti da un unico progetto .NET Visual Studio . Quando si sceglie un modello di progetto PCL, Visual Studio garantisce automaticamente che il codice utilizzi solo le librerie che esistono su ogni piattaforma di destinazione prescelta. Questo elimina la necessità di direttive per il preprocessore goffa e costruire bersagli multipli. D'altra parte, esso posto alcune restrizioni su cui le API è possibile chiamare dalla tua libreria; Mi spiego più su come ovviare a tali restrizioni in un attimo. Vedere "Cross-Platform con il .NET Framework di sviluppo" (msdn.microsoft.com/library/gg597391) per ulteriori dettagli sulle caratteristiche di PCL e utilizzare.

Tributo era una naturale adatta per raggiungere i miei obiettivi di cross-platform con Sterling. Sono stato in grado di refactoring oltre il 90 per cento della sterlina codebase in un unico comune PCL utilizzabili senza modifiche dal .NET Framework 4.5, Windows Phone 8 e Windows 8. Questo è un vantaggio enorme per la redditività a lungo termine del progetto.

Una breve nota sui progetti di unit test: A partire da oggi, non non c'è nessun equivalente PCL per codice di test di unità. Il principale ostacolo alla creazione di uno è la mancanza di un framework di test singola unità unificata che funziona su più piattaforme. Dato che la realtà, per i test unitari Sterling definiti progetti di test separato per .NET 4.5, Windows Phone 8 e Windows 8; il progetto 4.5 .NET contiene l'unica copia del codice di test, mentre altri progetti di condividono il codice di test utilizzando la tecnica di aggiungere come Link menzionata in precedenza. Ogni progetto di piattaforma riferimento agli assembly di quadro test unici per quella piattaforma; Fortunatamente, i nomi dello spazio dei nomi e tipo sono identici in ciascuna, così lo stesso codice compila invariato in tutti i progetti di test. Vedere la sterlina aggiornato codebase su GitHub (bit.ly/YdUNRN) per esempi di come funziona.

Sfruttare le API specifiche della piattaforma mentre tributo è di grande aiuto nella creazione di unificato basi di codice cross-platform, essi rappresentano un po ' di un dilemma: Come si usa API specifiche della piattaforma che non sono richiamabili da codice PCL? Un esempio perfetto è il refactoring del codice asincrono che ho accennato; mentre .NET 4.5 e Windows Store apps, in particolare, hanno una ricchezza di async potente API da scegliere, nessuno di questi è richiamabile da all'interno di un PCL. Si può avere la botte piena e mangiare troppo?

Si scopre che si può, con un po' di lavoro. L'idea è quella di definire all'interno del vostro PCL uno o più interfacce che modellare i comportamenti specifici della piattaforma che non riesci a chiamare direttamente e quindi implementano codice basato su PCL in termini di quelli interfaccia astrazioni. Quindi, nelle librerie separate specifiche della piattaforma, è fornire implementazioni per ogni interfaccia. Infine, in fase di esecuzione si creare istanze di tipi PCL per completare alcune attività, collegando l'implementazione dell'interfaccia specifica appropriata per la piattaforma di destinazione corrente. L'astrazione consente al codice PCL di rimanere disaccoppiato da dettagli piattaforma.

Se tutto questo suona vagamente familiare, dovrebbe: il modello che ho descritto qui è noto come Inversion of Control (IoC), una tecnica di progettazione software tried-and-true per il conseguimento di disaccoppiamento modulare e isolamento. Potete leggere di più su cio a bit.ly/13VBTpQ.

Mentre il porting Sterling, ho risolto diversi problemi di incompatibilità API utilizzando questo approccio. La maggior parte del problema API è venuto dallo spazio dei nomi System. Reflection. L'ironia è che, mentre ogni piattaforma di destinazione esposto tutte le funzionalità di riflessione che mi serviva per Sterling, ognuno aveva la propria peculiarità minori e sfumature che ha reso impossibile per sostenerli in modo uniforme nel tributo. Da qui la necessità di questa tecnica basata su cio. Troverete il conseguente astrazione interfaccia c# definito per Sterling ovviare a questi problemi a bit.ly/13FtFgO.

Un po ' di consigli generali

Ora che io ho descritto la mia strategia di migrazione per Sterling, mi prendo un piccolo passo indietro e considerare come le lezioni dall'esperienza potrebbero applicare nel caso generale.

Primo — e non sottolineerò mai abbastanza — utilizzare la funzionalità di PCL. Tributo è un'enorme vittoria per sviluppo cross-platform, e offrono sufficiente flessibilità di configurazione per soddisfare più alcun bisogno. Se si sta eseguendo la migrazione di una libreria esistente avanti (o anche scrivendo uno nuovo) e rivolge a più di una piattaforma, si deve usare un PCL.

Avanti, anticipare alcuni refactoring sforzo al fine di soddisfare gli obiettivi di progettazione aggiornato. In altre parole, non aspettatevi la migrazione di codice a un semplice processo meccanico, sostituendo una chiamata API con un altro. È interamente possibile che le modifiche sarà necessario fare andare più in profondità rispetto a livello della superficie e potrebbe richiedere una modifica uno o più core presupposti quando il codebase originale è stato scritto. C'è un limite pratico per la varianza totale che può imporre al codice esistente senza grande impatto a valle; dovrete decidere per te, dove è quella linea e quando e se per attraversarlo. La migrazione di una persona è di un'altra fonte codice forcella in un progetto completamente nuovo.

Infine, non abbandonare la casella degli strumenti esistente di modelli e tecniche di progettazione. Ho dimostrato come ho usato l'iniezione di principio e dipendenza cio con Sterling per approfittare delle API specifiche della piattaforma. Altri approcci simili senza dubbio vi servirà bene. Modelli di progettazione di software classico come strategia (bit.ly/Hhms), adattatore (bit.ly/xRM3i), metodo di modello (bit.ly/OrfyT) e facciata (bit.ly/gYAK9) può essere molto utile quando il refactoring del codice esistente per nuovi scopi.

Brave New Worlds

Il risultato finale del mio lavoro è un'implementazione completamente funzionale di Sterling NoSQL sulle piattaforme tre target del .NET Framework 4.5, Windows 8 e Windows Phone 8. È gratificante vedere Sterling eseguire sui dispositivi basati su Windows più recenti come il mio tablet superficie e il mio telefono Nokia Lumia 920.

Il progetto di Sterling è ospitato sul sito Wintellect GitHub (bit.ly/X5jmUh) e contiene il codice sorgente completo migrati come pure di unit test e progetti per ogni piattaforma del campione. Include anche un'implementazione del modello di driver Sterling che utilizza la tabella di archiviazione Windows Azure. Vi invito a clonare il repository di GitHub ed esplorare i modelli e le scelte di design che ho descritto in questo articolo; Mi auguro che servono come un utile punto di partenza per i vostri propri sforzi simili.

E ricordate, non buttare fuori quel vecchio codice... la migrazione!

Josh Lane è un consulente senior di Wintellect LLC in Atlanta. Egli ha trascorso 15 anni di architettura, progettazione e costruzione di software su piattaforme Microsoft e ha consegnato con successo una vasta gamma di soluzioni tecnologiche, dal call center, siti Web per compilatori JavaScript personalizzati e molti altri in mezzo. Egli gode la sfida di derivazione valore significativo business tramite software. Contattarlo al jlane@wintellect.com.

Grazie all'esperto tecnica seguente per la revisione di questo articolo: Jeremy Likness (Wintellect)