2012-05-22 19 views
5

Ho un'applicazione Windows Form con .NET 4 e Entity Framework per lo strato di dati ho bisogno di un metodo con la transazione, ma fare semplici test non ho potuto farlo funzionaretransazione Scope con Entity

In BLL:

public int Insert(List<Estrutura> lista) 
{ 
    using (TransactionScope scope = new TransactionScope()) 
    { 
      id = this._dal.Insert(lista); 
    } 
} 

In DAL:

public int Insert(List<Estrutura> lista) 
{ 
    using (Entities ctx = new Entities (ConnectionType.Custom)) 
    { 
    ctx.AddToEstrutura(lista); 
    ctx.SaveChanges(); //<---exception is thrown here 
    } 
} 

"Il provider sottostante fallito su Open."

Qualcuno ha qualche idea?

problema risolto - la mia soluzione

ho risolto il mio problema facendo alcuni cambiamenti. In uno dei miei DAL uso un inserto di massa e altre entità. La transazione problematica stava avvenendo per il fatto che la maggior parte della transazione (transazione sql) non comprendeva lo scope della transazione Quindi ho separato l'entità in DAL e usato la transazione sql nella sua esecuzione un po 'banale. ExecuteScalar();

Credo che non sia il modo più elegante per farlo, ma ho risolto la mia transazione problematica.

Ecco il codice della mia DAL

using (SqlConnection sourceConnection = new SqlConnection(Utils.ConnectionString())) 
    { 
     sourceConnection.Open(); 
     using (SqlTransaction transaction = sourceConnection.BeginTransaction()) 
     { 
      StringBuilder query = new StringBuilder(); 
      query.Append("INSERT INTO..."); 
      SqlCommand command = new SqlCommand(query.ToString(), sourceConnection, transaction); 
      using (SqlBulkCopy bulk = new SqlBulkCopy(sourceConnection, SqlBulkCopyOptions.KeepNulls, transaction)) 
      {       
       bulk.BulkCopyTimeout = int.MaxValue; 
       bulk.DestinationTableName = "TABLE_NAME"; 
       bulk.WriteToServer(myDataTable); 

       StringBuilder updateQuery = new StringBuilder(); 
       //another simple insert or update can be performed here 
       updateQuery.Append("UPDATE... "); 
       command.CommandText = updateQuery.ToString(); 
       command.Parameters.Clear(); 
       command.Parameters.AddWithValue("@SOME_PARAM", DateTime.Now); 
       command.ExecuteNonQuery(); 
       transaction.Commit(); 
      } 
     } 
    } 

grazie per l'aiuto

+0

possibile duplicato del [Il provider sottostante non è riuscita su Open] (http://stackoverflow.com/questions/2475008/the-underlying-provider-failed-on-open) Ha alcuni buoni suggerimenti relativi a connessioni/transazioni/DTC –

+1

Si sta utilizzando un anti-pattern qui. Tratta l'ObjectContext come unità di lavoro. – usr

risposta

-1

Invece di impiegare TransactionScope, è meglio impiegare modello UnitOfWork mentre si lavora con Entity Framework. fare riferimento a: unit of work pattern

e anche;

unit of work and persistance ignorance

+0

Il pattern 'UnitOfWork' non è una soluzione sostitutiva per' TransactionScope' –

+0

@EoinCampbell non è una sostituzione, ma è possibile utilizzare lo schema di UnitOfWork come un transactioncope; puoi chiarire perché la pensi così? – daryal

+0

Beh, per i principianti istanzia il suo 'TransactionScope' nel suo livello di applicazione e non nel suo DAL, quindi non è chiaro se il suo codice sia indicativo della realtà o se il TS possibilmente comprenda altre funzionalità.In secondo luogo, il pattern 'UOW' è utile solo se hai progettato il tuo DAL per supportarlo, ad esempio i singoli repository in grado di acquisire un'istanza ObjectContext durante la costruzione. Quindi stai suggerendo una revisione piuttosto drastica invece di risolvere il problema che ha postato. E infine, ma probabilmente la cosa più importante ... Non è possibile eseguire il rollback di UnitOfWork a meno che non si stia utilizzando ... Transazioni –

1

Secondo l'Eccelso Google, sembra che EF strette relazioni si aprirà/con ogni chiamata a un database. Dal momento che lo fa, tratterà la transazione come utilizzando più connessioni (utilizzando una transazione distribuita). Il modo per aggirare questo è quello di aprire e chiudere la connessione manualmente quando lo si utilizza.

Ecco le informazioni sul distributed transactions issue.

Ecco come manually open and close the connection.

Un piccolo esempio di codice:

public int Insert(List<Estrutura> lista) 
{ 
    using (TransactionScope scope = new TransactionScope()) 
    { 
     using (Entities ctx = new Entities (ConnectionType.Custom)) 
     { 
      ctx.Connection.Open() 

      id = this._dal.Insert(ctx, lista); 
     } 
    } 
} 

public int Insert(Entities ctx, List<Estrutura> lista) 
{ 
    ctx.AddToEstrutura(lista); 
    ctx.SaveChanges(); 
}