2010-01-21 15 views
6

Ho un metodo che voglio essere "transazionale" in senso astratto. Chiama due metodi che capita di fare cose con il database, ma questo metodo non lo sa.conosce troppo i metodi che sta chiamando

public void DoOperation() 
{ 
    using (var tx = new TransactionScope()) 
    { 
     Method1(); 
     Method2(); 

     tc.Complete(); 
    } 
} 

public void Method1() 
{ 
    using (var connection = new DbConnectionScope()) 
    { 
     // Write some data here 
    } 
} 

public void Method2() 
{ 
    using (var connection = new DbConnectionScope()) 
    { 
     // Update some data here 
    } 
} 

perché in termini reali del TransactionScope significa che una transazione di database verrà utilizzato, abbiamo un problema in cui si potrebbe essere promosso a una transazione distribuita, se otteniamo due connessioni diverse dalla piscina.

ho potuto risolvere questo avvolgendo il metodo DoOperation() in un ConnectionScope:

public void DoOperation() 
{ 
    using (var tx = new TransactionScope()) 
    using (var connection = new DbConnectionScope()) 
    { 
     Method1(); 
     Method2(); 

     tc.Complete(); 
    } 
} 

ho fatto DbConnectionScope me stesso per una simile scopo, in modo che io non devo passare oggetti di connessione al sub-modalità (questo è un esempio più elaborato del mio vero problema). Ho avuto l'idea da questo articolo: http://msdn.microsoft.com/en-us/magazine/cc300805.aspx

Tuttavia non mi piace questa soluzione in quanto significa DoOperation ora ha conoscenza che i metodi che è chiamata può utilizzare una connessione (ed eventualmente un collegamento diverso ogni). Come potrei refactoring questo per risolvere il problema?

Un'idea che sto pensando di sta creando un più generale OperationScope, in modo che quando fatto squadra con un costume stile di vita Castello di Windsor scriverò, significherà qualsiasi componente richiesto del contenitore con OperationScopeLifetyle sarà sempre ottenere lo stesso istanza di quel componente. Questo risolve il problema perché OperationScope è più ambiguo di DbConnectionScope.

+0

Cosa? Qualcuno ha votato affinché questa domanda sia chiusa? Perché? –

risposta

1

Qui vedo requisiti in conflitto.

Da un lato, non si desidera che DoOperation abbia consapevolezza del fatto che per le sue operazioni secondarie viene utilizzata una connessione al database.

D'altra parte, chiaramente è consapevole di questo fatto perché utilizza uno TransactionScope.

posso sorta di capire quello che stai ricevendo in quando dici che vuoi che sia transazionale nel senso astratto, ma il mio prendere su questo è che è praticamente impossibile (no, graffio che - completamente impossibile) per descrivere una transazione in termini così astratti. Diciamo solo dire di avere una classe come questa:

class ConvolutedBusinessLogic 
{ 
    public void Splork(MyWidget widget) 
    { 
     if (widget.Validate()) 
     { 
      widgetRepository.Save(widget); 
      widget.LastSaved = DateTime.Now; 
      OnSaved(new WidgetSavedEventArgs(widget)); 
     } 
     else 
     { 
      Log.Error("Could not save MyWidget due to a validation error."); 
      SendEmailAlert(new WidgetValidationAlert(widget)); 
     } 
    } 
} 

Questa classe sta facendo almeno due cose che probabilmente non può essere il rollback (impostando la proprietà di una classe e l'esecuzione di un gestore di eventi, che potrebbe ad esempio cascade-aggiorna alcuni controlli su un modulo), e almeno altre due cose che sicuramente non possono essere ripristinate (aggiungendo un file di registro da qualche parte e inviando un avviso via e-mail).

Forse questo sembra un esempio forzato, ma questo è in realtà il mio punto; non è possibile trattare un TransactionScope come una "scatola nera".Lo scopo è in effetti una dipendenza come qualsiasi altra; TransactionScope fornisce solo un'astrazione conveniente per un'unità di lavoro che potrebbe non essere sempre appropriata in quanto non racchiude in realtà una connessione al database e non è in grado di prevedere il futuro. In particolare, in genere non è appropriato quando una singola operazione logica deve estendersi su più di una connessione di database, sia che queste connessioni siano allo stesso database o a quelle diverse. Cerca di gestire questo caso, ovviamente, ma come hai già imparato, il risultato non è ottimale.

Il mio modo di vedere, si hanno diverse possibilità:

  1. rendere esplicito il fatto che Method1 e Method2 richiedono un collegamento facendoli prendere un parametro di connessione, o da loro refactoring in una classe che accetta una dipendenza di connessione (costruttore o proprietà). In questo modo, la connessione diventa parte del contratto , quindi Method1 non sa più troppo - sa esattamente cosa deve sapere in base al progetto.

  2. Accetta che il metodo DoOperationfa hanno la consapevolezza di ciò Method1 e Method2 fanno. Infatti, non c'è niente di sbagliato in questo! È vero che non si vuole fare affidamento sui dettagli di implementazione di alcune chiamate future, ma le dipendenze in avanti di nell'astrazione sono generalmente considerate OK; è invertire le dipendenze di di cui si deve preoccuparsi, come quando alcune classi nel profondo del modello di dominio cercano di aggiornare un controllo dell'interfaccia utente che non ha alcuna conoscenza aziendale in primo luogo.

  3. Utilizzare uno schema Unit of Work più robusto (anche: here). Questo sta diventando sempre più popolare ed è, in generale, la direzione in cui Microsoft è passata da Linq a SQL ed EF (le versioni DataContext/ObjectContext sono fondamentalmente implementazioni UOW). Questo sistema si integra bene con un framework DI e in sostanza elimina l'esigenza di preoccuparsi di quando le transazioni iniziano e finiscono e di come deve avvenire l'accesso ai dati (il termine è "ignoranza della persistenza"). Ciò richiederebbe probabilmente una rielaborazione significativa del tuo progetto, ma la sterlina per sterlina sarà la più facile da mantenere a lungo termine.

Spero che uno di quelli ti aiuti.

+0

Penso che la cosa "dipendenza diretta" sia ciò che lo fa o lo spezza. UoW è sicuramente il modo in cui normalmente mi avvicino a questo, ma sto lavorando a un'app legacy che utilizza i data-set (nemmeno i set di dati), quindi è complicato. Grazie per la risposta, mi sento un po 'più a mio agio sulla base della seconda parte della risposta. –

0

Sarebbe possibile trasferire la transazione di transazione/arruolarsi nel DB e rimuoverlo interamente dal codice?

+0

Non proprio. In questo caso vengono emessi due comandi DB completamente separati, ma in questo caso voglio che entrambi o nessuno dei due esegua il commit se uno o l'altro non riesce o se qualcos'altro lancia un'eccezione. –

Problemi correlati