Un Transaction Manager in un sistema nTier

Ormai è un po' di tempo che sto realizzando una piccola applicazione con architettura a tre livelli, e mi si è posto subito un problema, ossia gestire una transazione tra più entity.

Per capirci, io ho la mia entity Fattura che al suo interno ha una collection di righeFattura, la entity Cliente e la entity Fornitore (direi che per l'esempio bastano).
Quando devo persistere la mia entity (con le rispettive sottoentity) devo avere la certezza che tutto sia andato a buon fine e nel caso si verifichi un errore si deve ripristinare la situazione originale e avvertire l'utente che qualcosa è andatao storto.

Per risolvere la il problema ho deciso di implementare questa soluzione:

  1. Ogni classe di persistenza del DAL deve esporre un'interfaccia ITransactionable;
  2. Il DAL deve esporre una classe che implementa un'interfaccia ITransactionManager;

 

I seguenti diagrammi mostrano le interfacce nel dettaglio:

 


 

Ora all'interno delle nostre classi di persistenza dove esponiamo ITransactionable prima della chiamata di update, delete, ecc dobbiamo verificare se l'oggetto transazionale è instanziato oppure no, lo possiamo fare con una semplice if.

public bool Update(Fattura item)
{
int rowsAffected;

if (Transaction == null)
{
rowsAffected = SqlHelper.ExecuteNonQuery(
ProviderHelper.ConnectionString,
CommandType.StoredProcedure,
"sp_Fattura_Update", CreateParameter(true, item));
}
else
{
rowsAffected = SqlHelper.ExecuteNonQuery(
Transaction,
CommandType.StoredProcedure,
"sp_Fattura_Update", CreateParameter(true, item));

}

if (rowsAffected <= 0)
throw new Domain.Exception.ConcurrencyException(Resource.Res.GetString("ConcurrencyException"));
else
return true;
} 

 

 

Transaction non è l'implementazione del'interfaccia ITransactionable ma un suo cast a SqlTransaction.

Il nostro Transaction Manager presente nel DAL sarà pittusto semplice quindi una roba tipo questa: 

private SqlConnection conn;
private SqlTransaction transaction;
private List coll;


public TransactionManager()
{
coll = new List();
}

 

public void Add(ITransactionable transactionObject)
{
coll.Add(transactionObject);
}


public void Remove(ITransactionable transactionObject)
{

transactionObject.ExternalTransaction = null;
coll.Remove(transactionObject);
}


public void StartTransaction()
{
conn = new SqlConnection(ProviderHelper.ConnectionString);
conn.Open();
transaction = conn.BeginTransaction();

for (int i = 0; i < coll.Count; i++)
coll[i].ExternalTransaction = transaction;


}


public void Commit()
{
transaction.Commit();
}


public void Rollback()
{
transaction.Rollback();
}

public void Dispose()
{
if(conn.State == ConnectionState.Open)
conn.Close();
for (int i = 0; i < coll.Count; i++)
{
coll[i].ExternalTransaction = null;
coll.RemoveAt(i);
}
transaction.Dispose();
} 

 

Dal Business Layer vado ad utilizzare il Transaction Manager in questo modo: 

using (ITransactionManager manager = AbstractFactory.ProviderFactory.TransactionManager)
{

IFatturaDataProvider aProvider = AbstractFactory.ProviderFactory.FatturaProvider;
IFornitoreDataProvider fProvider = AbstractFactory.ProviderFactory.FornitoreProvider;
IClienteDataProvider cProvider = AbstractFactory.ProviderFactory.ClienteProvider;

 

manager.Add(provider);
manager.Add(fProvider);
manager.Add(cProvider);

manager.StartTransaction();

try
{
provider.Update(item);
cProvider.Update(item.Cliente);
fProvider.Update(item.Fornitore);

manager.Commit();
}
catch
{
manager.Rollback();
throw;
}

}

 

Secondo voi l'implementazione è quella corretta?

 

 

 


Comments