2014-05-03 7 views
6

Abbiamo un DB aziendale che viene replicato attraverso molti siti in tutto il mondo. Vorremmo che la nostra app tenti di connettersi a uno dei siti locali e, se il sito non è attivo, vogliamo che ricada nel DB aziendale. Vorremmo questo comportamento su ciascuna delle nostre operazioni di DB.Schema generale per il failover da un database a un altro utilizzando Entity Framework?

Stiamo utilizzando Entity Framework, C# e SQL Server.

All'inizio speravo di poter specificare solo un "partner di failover" nella stringa di connessione, ma che funziona solo in un ambiente DB con mirroring, che non lo è. Ho anche esaminato la scrittura di una IDbExecutionStrategy personalizzata. Ma queste strategie consentono solo di specificare il modello per riprovare un'operazione DB fallita. Non ti permette di cambiare l'operazione in alcun modo come indirizzarla a una nuova connessione.

Quindi, è a conoscenza di qualsiasi schema valido per gestire questo tipo di operazione, oltre alla duplicazione della logica di riprova attorno a ciascuna delle nostre numerose operazioni DB?


Aggiornamento sulla 2014/05/14:

io elaboro in risposta ad alcune delle proposte già fatte.

devo molti luoghi in cui il codice simile al seguente:

try 
{ 
    using(var db = new MyDBContext(ConnectionString)) 
    { 
     // Database operations here. 
     // var myList = db.MyTable.Select(...), etc. 
    } 
} 
catch(Exception ex) 
{ 
    // Log exception here, perhaps rethrow. 
} 

è stato suggerito che ho una routine che prima controlla ognuna delle corde connessioni e restituisce il primo che si connette con successo. Questo è ragionevole fin dove arriva. Ma alcuni degli errori che vedo sono timeout sulle operazioni, in cui la connessione funziona ma il DB ha problemi che impediscono il completamento dell'operazione.

Quello che sto cercando è un pattern che posso usare per incapsulare l'unità di lavoro e dire "Prova questo sul primo database." Se fallisce per qualsiasi motivo, esegui il rollback e provalo sul secondo DB. che fallisce, provalo sul terzo, ecc. fino a quando l'operazione ha successo o non hai più DB. " Sono abbastanza sicuro di poter girare il mio (e posterò il risultato se lo faccio), ma speravo che potesse esserci un modo noto per avvicinarsi a questo.

+0

Potrei chiedere che venga spostato in dba.stackexchange.com, ma penso davvero che si tratti più di una domanda di programmazione/Entity Framework piuttosto che di una di amministrazione DB. Mi sbaglio? –

+0

Sì, posso vederlo. Ignora la mia precedente affermazione :) – LittleBobbyTables

+2

Quale sarebbe il tuo approccio alle transazioni allora? Se un'operazione fallita all'interno di una transazione è passata al db fallback nel mezzo della transazione? –

risposta

0

Ecco quello che ho finito di attuazione, a grandi pennellate:

Definire i delegati chiamati UnitOfWorkMethod che verranno eseguiti una sola unità di lavoro sulla base di dati, in una singola transazione. Prende una stringa di connessione e uno restituisce anche un valore:

delegate T UnitOfWorkMethod<out T>(string connectionString); 
delegate void UnitOfWorkMethod(string connectionString); 

Definire un metodo chiamato ExecuteUOW, che avrà un'unità di lavoro e metodo cercare di eseguirlo utilizzando la stringa di connessione preferita. Se fallisce, si cerca di eseguirlo con la stringa di connessione seguente:

protected T ExecuteUOW<T>(UnitOfWorkMethod<T> method) 
{ 
    // GET THE LIST OF CONNECTION STRINGS 
    IEnumerable<string> connectionStringList = ConnectionStringProvider.GetConnectionStringList(); 

    // WHILE THERE ARE STILL DATABASES TO TRY, AND WE HAVEN'T DEFINITIVELY SUCCEDED OR FAILED 
    var uowState = UOWStateEnum.InProcess; 
    IEnumerator<string> stringIterator = connectionStringList.GetEnumerator(); 
    T returnVal = default(T); 
    Exception lastException = null; 
    string connectionString = null; 
    while ((uowState == UOWStateEnum.InProcess) && stringIterator.MoveNext()) 
    { 
     try 
     { 
      // TRY TO EXECUTE THE UNIT OF WORK AGAINST THE DB. 
      connectionString = stringIterator.Current; 
      returnVal = method(connectionString); 
      uowState = UOWStateEnum.Success; 
     } 
     catch (Exception ex) 
     { 
      lastException = ex; 
      // IF IT FAILED BECAUSE OF A TRANSIENT EXCEPTION, 
      if (TransientChecker.IsTransient(ex)) 
      { 
       // LOG THE EXCEPTION AND TRY AGAINST ANOTHER DB. 
       Log.TransientDBException(ex, connectionString); 
      } 
       // ELSE 
      else 
      { 
       // CONSIDER THE UOW FAILED. 
       uowState = UOWStateEnum.Failed; 
      } 
     } 
    } 

    // LOG THE FAILURE IF WE HAVE NOT SUCCEEDED. 
    if (uowState != UOWStateEnum.Success) 
    { 
     Log.ExceptionDuringDataAccess(lastException); 
     returnVal = default(T); 
    } 
    return returnVal; 
} 

Infine, per ogni operazione definiamo la nostra unità di metodo di lavoro delegato. Ecco un esempio

UnitOfWorkMethod uowMethod = 
    (providerConnectionString => 
    { 
     using (var db = new MyContext(providerConnectionString)) 
     { 
      // Do my DB commands here. They will roll back if exception thrown. 
     } 
    }); 
ExecuteUOW(uowMethod); 

Quando ExecuteUOW viene chiamato, si cerca il delegato in ogni database fino a quando non sia esito positivo o negativo su ognuna di esse.

Accetterò questa risposta poiché affronta completamente tutte le preoccupazioni sollevate nella domanda originale. Tuttavia, se qualcuno fornisce e risponde che è più elegante, comprensibile o corregge i difetti in questo caso, lo accetterò felicemente.

Grazie a tutti coloro che hanno risposto.

0

Come utilizzare un sistema di Dependency Injection come autofac e registrando una factory per nuovi oggetti di contesto, eseguirà una logica che proverà a connettersi prima a local e in caso di errore si collegherà al db aziendale. Quindi restituirà l'oggetto DbContext pronto. Questo stabilimento verrà fornito a tutti gli oggetti che lo richiedono con il sistema di iniezione delle dipendenze: lo utilizzeranno per creare contesti e smaltirli quando non saranno più necessari.

+0

Grazie per la risposta. Questo potrebbe essere l'inizio di quello che sto cercando, ma sto cercando qualcosa che possa gestire i fallimenti durante l'operazione. Ho aggiornato la domanda per chiarire. –

0

"Vorremmo che la nostra app tenti di connettersi a uno dei siti locali e, se il sito non è attivo, vogliamo che ricada nel DB aziendale. Ci piacerebbe questo comportamento su ciascuna delle nostre operazioni DB ".

Se la tua app è di sola lettura sul DB e la coerenza dei dati non è assolutamente vitale per la tua app/utenti, allora è solo una questione di provare a CONNECT fino a quando un sito operativo è stato trovato. Come suggerito da M. Ali nella sua osservazione.

Altrimenti, ti suggerisco di smettere di pensare su questa linea immediatamente perché stai solo correndo a 90 mph in una strada senza uscita. Come ha suggerito Viktor Zychla nella sua osservazione.

+0

Grazie per la risposta. Sono d'accordo sul fatto che ciò che cerco non sarebbe pratico se mi interessassi delle transazioni che riguardano più unità di lavoro. E sono anche d'accordo che potrei mettere la logica wrapper su ogni UOW per provare a connettermi, quindi utilizzare il primo DB che funziona. Ma quello che sto cercando è un po 'di più, in più vorrei evitare di duplicare questa logica con ogni UOW. Aggiornerò la domanda originale per l'elaborazione. –

Problemi correlati