NHibernate with Autofac dans ASP.NET (MVC): ITransaction
-
06-07-2019 - |
Question
Quelle est la meilleure approche pour gérer les transactions NHibernate à l’aide de Autofac dans une application Web?
Mon approche de la session est
builder.Register(c => c.Resolve<ISessionFactory>().OpenSession())
.ContainerScoped();
Pour ITransaction
, j'ai a trouvé un exemple sur Google Code, mais il s’appuie sur HttpContext.Current.Error
pour décider de l’annulation.
Y a-t-il une meilleure solution? Et quelle doit être l’étendue de la transaction NHibernate ?
La solution
J'ai posté ceci il y a quelque temps:
Modifié, de sorte que l'intercepteur dispose d'une capacité de journalisation et que l'attribut [Transaction] puisse également être utilisé sur une classe.
[global::System.AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class TransactionAttribute : Attribute
{
}
public class ServicesInterceptor : Castle.Core.Interceptor.IInterceptor
{
private readonly ISession db;
private ITransaction transaction = null;
public ServicesInterceptor(ISession db)
{
this.db = db;
}
public void Intercept(IInvocation invocation)
{
ILog log = LogManager.GetLogger(string.Format("{0}.{1}", invocation.Method.DeclaringType.FullName, invocation.Method.Name));
bool isTransactional = IsTransactional(invocation.Method);
bool iAmTheFirst = false;
if (transaction == null && isTransactional)
{
transaction = db.BeginTransaction();
iAmTheFirst = true;
}
try
{
invocation.Proceed();
if (iAmTheFirst)
{
iAmTheFirst = false;
transaction.Commit();
transaction = null;
}
}
catch (Exception ex)
{
if (iAmTheFirst)
{
iAmTheFirst = false;
transaction.Rollback();
db.Clear();
transaction = null;
}
log.Error(ex);
throw ex;
}
}
private bool IsTransactional(MethodInfo mi)
{
var atrClass = mi.DeclaringType.GetCustomAttributes(false);
foreach (var a in atrClass)
if (a is TransactionAttribute)
return true;
var atrMethod = mi.GetCustomAttributes(false);
foreach (var a in atrMethod)
if (a is TransactionAttribute)
return true;
return false;
}
}
Autres conseils
Lorsque j'utilise autofac, j'utilise la même méthode de conteneur contourné, mais au lieu de transmettre la même session à mes objets Repository / DAO, je passe un UnitOfWork dont le contenu est de conteneur. L’unité de travail a cela dans le constructeur.
private readonly ISession _session;
private ITransaction _transaction;
public UnitOfWork(ISession session)
{
_session = session;
_transaction = session.BeginTransaction();
}
Et la disposition est:
public void Dispose()
{
try
{
if (_transaction != null &&
!_transaction.WasCommitted &&
!_transaction.WasRolledBack)
_transaction.Commit();
_transaction = null;
}
catch (Exception)
{
Rollback();
throw;
}
}
J'utilise (ab) le système d'élimination déterministe dans autofac pour gérer cela, et bien je l'aime bien.
L’autre chose est que je ne fais que cibler un environnement ASPNet et que j’ai délibérément décidé qu’une transaction était liée à une requête Web. Donc, une transaction par modèle de requête Web.
Pour cette raison, je peux faire cette erreur dans le code de traitement d'un IHttpModule:
void context_Error(object sender, System.EventArgs e)
{
_containerProvider.RequestContainer.Resolve<IUnitOfWork>().Rollback();
}
Je n’ai pas jeté un œil sur NHibernate.Burrow de trop près, mais je suis sûr qu’il ya quelque chose qui fait l’essentiel de cela.
Je gère habituellement moi-même la transaction.
public ActionResult Edit(Question q){
try {
using (var t = repo.BeginTransaction()){
repo.Save(q);
t.Commit();
return View();
}
catch (Exception e){
...
}
}