2009-07-14 12 views
7

La mia situazione è che ho fottuto essenzialmente. Ho ereditato la mia base di codice circa 1,5 anni fa quando ho preso questa posizione e piuttosto che reinventare la ruota, anche se ora so che avrei dovuto, ho mantenuto il DAL praticamente nella stessa struttura del precedente sviluppatore.Quale strategia DAL usi o suggerisci?

Essenzialmente c'è un file (ora a 15k linee di codice) che funge da passaggio tra un gruppo di DAO che utilizzano DataSet e TableAdapter per recuperare i dati. I miei file xsd sono cresciuti a tal punto da far sì che R # subisca un arresto anomalo allo studio visivo ogni volta che si apre e la classe intermedia che ora è di 15k linee impiega anche un'eternità per analizzare R #. Per non dire che è brutto, funziona ma non bene, ed è un incubo assoluto per il debug.

Quello che ho provato fino ad ora è passare a NHibernate. NHibernate è una grande libreria, ma sfortunatamente non era abbastanza adattabile per funzionare con la mia applicazione, da quello che dice lo sviluppatore principale (Fabio Maulo) è praticamente una combinazione dei miei requisiti applicativi e delle restrizioni su NHibernate quando si usa l'identità come database Strategia PK.

Così ora sono tornato a progettare essenzialmente il mio DAL. Sto osservando alcuni modelli diversi per questo, ma vorrei ottenere le tue strategie di progettazione DAL. Ci sono tanti modi e motivi per implementare un DAL in un modo particolare, quindi se potessi spiegare la tua strategia e perché era la soluzione migliore per te, lo apprezzerei molto.

Grazie in anticipo!

Modifica: mi permetta di spiegare perché NHibernate non ha funzionato poiché quella sembra essere la risposta immediata. I miei utenti creano un "lavoro" che in realtà è solo una rappresentazione transitoria della mia classe di lavoro. All'interno di questo lavoro gli daranno uno o un elenco di fattori di peso che sono anche transitori al momento della creazione. Infine forniscono un elenco di dettagli del lavoro a cui è associato un particolare fattore di peso. Perché, nel DB, i fattori di peso sono unici quando vado a perseguire il lavoro e si riduce a un fattore di peso che muore quando trova un fattore di peso duplicato. Ho provato a eseguire un controllo prima di assegnare il fattore peso al dettaglio (che non volevo fare perché non voglio le chiamate extra al db) ma chiamare CreateCriteria in NH provoca anche un flush nella sessione, secondo Fabio, che distrugge la mia cache e quindi uccide l'intera rappresentazione in memoria del lavoro. La gente alla mailing list NH ha detto che dovrei passare a GUID, ma questa non è un'opzione praticabile in quanto il processo di conversione sarebbe un incubo.

+1

Quali problemi hai con NHibernate? Lo uso sempre con i PK di identità senza problemi. –

+1

Hmm, forse usa Sessioni di lunga durata (modello Session per Business Transaction), e in tale approccio, l'uso dell'identità è scoraggiato, dal momento che rompe l'unità di lavoro (deve essere svuotato direttamente dopo l'inserimento di una nuova entità). Una soluzione potrebbe essere quella di eliminare l'identità e utilizzare il generatore di identità HiLo. –

+0

Ah sì, questa è una possibilità (e la tua soluzione). –

risposta

1

La mia esperienza con NHibernate è che, sebbene sia ricco di funzionalità e prestazioni molto elevate, alla fine sarà necessario diventare esperti di NHibernate per correggere alcuni comportamenti imprevisti. Leggendo le risposte pro-NHibernate e vedere

Hmm, forse egli utilizza in esecuzione lungo Sessions (Session per Business modello di transazione), e in un tale approccio , utilizzando l'identità è scoraggiato, dal momento che spezza il unitofwork (ha bisogno di svuotare direttamente dopo aver inserito una nuova entità). Una soluzione potrebbe essere quella di eliminare l'identità e utilizzare il generatore di identità HiLo .

illustra esattamente cosa intendo.

Quello che ho fatto è creare una classe base modellata in qualche modo al di fuori del pattern ActiveRecord, che eredito da e contrassegno la classe ereditata con attributi che la collegano ad una stored procedure ciascuno per Seleziona, Inserisci, Aggiorna ed Elimina . La classe base utilizza Reflection per leggere gli attributi e assegnare i valori delle proprietà della classe ai parametri SP e, nel caso di Select(), assegnare i valori delle colonne di SQLDataReader dei risultati alle proprietà di un elenco di generici.

Questo è ciò che appare come DataObjectBase:

interface IDataObjectBase<T> 
    { 
     void Delete(); 
     void Insert(); 
     System.Collections.Generic.List<T> Select(); 
     void Update(); 
    } 

Questo è un esempio di una classe di dati che ne derivano:

[StoredProcedure("usp_refund_CustRefundDetailInsert", OperationType.Insert)] 
    [StoredProcedure("usp_refund_CustRefundDetailSelect", OperationType.Select)] 
    [StoredProcedure("usp_refund_CustRefundDetailUpdate", OperationType.Update)] 
    public class RefundDetail : DataObjectBase<RefundDetail> 
    { 

     [StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Output)] 
     [StoredProcedureParameter(null, OperationType.Select, ParameterDirection.Input)] 
     [ResultColumn(null)] 
     public int? RefundDetailId 
     { get; set; } 

     [StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Select, ParameterDirection.Input)] 
     [ResultColumn(null)] 
     public int? RefundId 
     { get; set; } 
     [StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)] 
     [ResultColumn(null)] 
     public int RefundTypeId 
     { get; set; } 

     [StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)] 
     [ResultColumn(null)] 
     public decimal? RefundAmount 
     { get; set; }   
     [StoredProcedureParameter(null, OperationType.Update, ParameterDirection.Input)] 
     [StoredProcedureParameter(null, OperationType.Insert, ParameterDirection.Input)] 
     [ResultColumn(null)] 
     public string ARTranId 
     { get; set; } 

    } 

So che sembra come se fossi reinventare la ruota, ma tutte le librerie che ho trovato avevano troppa dipendenza da altre librerie (ActiveRecord + NHibernate, ad esempio, che era un secondo vicino) o erano troppo complicate da usare e amministrare.

La libreria che ho creato è molto leggera (forse un paio di centinaia di righe di C#) e non fa altro che assegnare valori ai parametri ed eseguire l'SP. Si presta anche molto bene alla generazione del codice, quindi alla fine mi aspetto di scrivere nessun codice di accesso ai dati. Mi piace anche che usi un'istanza di classe invece di una classe statica, in modo che io possa passare i dati alle query senza qualche scomoda raccolta di criteri o HQL. Select() significa "diventa più come me".

+0

Questo è interessante, è un po 'prolisso, ma molto interessante.La tua strategia ti permette di caching? – joshlrogers

+0

un'altra cosa: anche questo funziona alla grande con una libreria di stored procedure esistenti. non sono richiesti che vengano nominati in un certo modo o che abbiano nomi o ordini di parametri non arbitrari –

+0

Sono un VB di vecchia scuola, verbose è la mia merda :) Non c'è ancora un caching integrato (ancora). Mi sto prendendo gioco dell'idea di utilizzare PostSharp per implementare la cache in stile AOP intercettando le chiamate di metodo e restituendo dati predefiniti. Per ora, questo è anni luce avanti rispetto al raw ADO.net che stavano facendo. –

0

Se il DAL è scritto su un'interfaccia, sarebbe molto più facile passare a NHibernate o qualcosa di comperabile (preferirei Fluent-NHibernate, ma sto divagando). Quindi, perché non passare il tempo invece a refactoring del DAL per usare un'interfaccia, e quindi scrivere una nuova implementazione usando NH o il tuo ORM di scelta?

1

Per me la soluzione migliore era un concetto piuttosto semplice: utilizzare le definizioni di classe DAO e con la riflessione creare tutto il codice SQL necessario per compilarli e salvarli. In questo modo non esiste un file di mappatura, solo classi semplici. I miei DAO richiedono una classe di base Entity quindi non è un POCO ma non mi disturba. Supporta qualsiasi tipo di chiave primaria, sia singola colonna di identità o multi colonna.

+0

Utilizzate questo in combinazione con un Meta Data Mapper e/o un Data Mapper? Inoltre, come gestisci le lezioni di Joins o di riferimento? – joshlrogers

+1

@joshlrogers Nessun data mapper - ogni tabella è rappresentata da una classe DAO. I join vengono definiti implementando un metodo GetJoin in ogni DAO che restituisce l'istruzione di join appropriata in base alle altre tabelle. Ogni DAO unito ha un elenco o un oggetto per la tabella unita. I join vengono definiti concatenando i costruttori al tempo di query -esempio: Elenco = SQL.Read (searchCriteria, new (Products (new Orders())) restituisce un join tra Products and Orders –

0

In progetti recenti abbiamo interrotto la programmazione di un DAL separato.

Invece utilizziamo un Object Relational Mapper (nel nostro caso Entity Framework). Quindi lasciamo il programma del livello aziendale direttamente contro l'ORM.

Ciò ci ha risparmiato oltre il 90% dello sforzo di sviluppo in alcuni casi.

+0

Questo è esattamente il motivo per cui ho voluto passare a NHibernate Sfortunatamente non ha funzionato – joshlrogers

0

Il mio primo passo sarebbe quello di spezzare il codice di un mostro da 15 KLOC, quindi elaborare una strategia per la creazione di un nuovo DAL.

0

Linq to SQL è utile se si utilizza SQL Server. C'è una fonte là fuori per un provider LinqToSQL per Access e MySQL. Non l'ho provato però. LinqToSql segue il modello di UnitOfWork che è simile al modo in cui funziona ADO.NET. Esegui una serie di modifiche a una copia locale dei dati, quindi impegni tutte le modifiche con una chiamata di aggiornamento. È abbastanza pulito, penso.

È anche possibile estendere la classe DataRow per fornire un accesso fortemente digitato ai campi. Ho usato XSLT per generare i discendenti DataRow in base ai metadati di ogni tabella. Ho un decimo DataTable generico. MyDataTable dove T è la mia riga derivata. So che i dataset fortemente tipizzati di MS fanno una cosa simile ma volevo una versione generica leggera che ho il controllo completo di. Una volta che hai questo, puoi scrivere metodi di accesso statico che interrogano il db e riempiono il DataTable.

Sareste incaricati di scrivere le modifiche da DataTable a DataSource. Vorrei scrivere una classe generica o un metodo che crea l'aggiornamento, inserisce ed elimina.

Buona fortuna!

0

Uso il wrapper mine per SP per il recupero dei dati più veloce e L2S quando la perfomance non è un obiettivo. Il mio DAL utilizza pattern di repository e logica incapsulata per TDD.

Problemi correlati