Domanda

Ho la seguente implementazione e vorrei un feedback sul corretto utilizzo di NHibernate per sessioni e transazioni.

public interface IUnitOfWork : IDisposable
{
    ISession CurrentSession { get; }
    void Commit();
    void Rollback();
}

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory _sessionFactory;
    private readonly ITransaction _transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
        CurrentSession = _sessionFactory.OpenSession();
        _transaction = CurrentSession.BeginTransaction();
    }

    public ISession CurrentSession { get; private set; }

    public void Dispose()
    {
        CurrentSession.Close();
        CurrentSession = null;
    }

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

    public void Rollback()
    {
        if (_transaction.IsActive) _transaction.Rollback();
    }
}

Rilegatura Ninject

Bind<IUnitOfWork>().To<UnitOfWork>().InTransientScope();
Bind<ISessionFactory>().ToProvider<NHibernateSessionFactoryProvider>().InSingletonScope();
Bind<IRepository>().To<Repository>().InTransientScope();

Ecco un esempio dell'utilizzo:

public class Repository : IRepository
{
    private readonly ISessionFactory _sessionFactory;

    public Repository(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void Add(IObj obj)
    {
        using (var unitOfWork = new UnitOfWork(_sessionFactory))
        {
            unitOfWork.CurrentSession.Save(obj);
            unitOfWork.Commit();
        }         
    }
}

Nella mia precedente implementazione avrei inserito IUnitOfWork nel mio costruttore di repository in questo modo

public Repository(IUnitOfWork unitOfWork)
    {...

Ma il metodo Dispose() non veniva eseguito facendo sì che una chiamata successiva generasse questa eccezione:"Impossibile accedere a un oggetto eliminato.Nome oggetto:'AdoTransaction'."

È stato utile?

Soluzione

Prima osservazione: il repository non deve commettere l'unità di lavoro. Questo sconfigge il punto di tutta l'unità di modello di lavoro. Con immediatamente salvare le modifiche all'interno del repository, si è "micro gestire" la Sessione NHibernate.

L'unità di lavoro dovrebbe fare riferimento più in alto lo stack, nel vostro livello di applicazione / servizio. Questo permette di avere il codice di applicazione che esegue diverse azioni, potenzialmente in diversi repository, e comunque alla fine si impegnano tutto in una volta.

La classe UnitOfWork stesso sembra ok, anche se ci si dovrebbe chiedere se si ha realmente bisogno. Nel NHibernate, l'ISession è la vostra unità di lavoro. La classe UnitOfWork non sembra aggiungere un sacco di valore (soprattutto perché si sta esponendo la proprietà CurrentSession comunque)

Ma si ha bisogno di pensarci di vita. Penso che tu abbia sbagliato su questo punto. gestione della durata della sessione dipende dal tipo di applicazione che si sta sviluppando: in una web app, in genere si vuole avere un'unità di lavoro per richiesta (si potrebbe desiderare di Google su 'sessione di NHibernate per richiesta'). In un'applicazione desktop che è un po 'più complicato, è più parte del tempo volere una 'session per schermo' o 'conversazione per ogni transazione di business'.

Altri suggerimenti

Ho un tipo prevalentemente CRUD di applicazione, e ho realizzato l'unità di lavoro con il modello Repository, ma non potrebbe davvero allontanarsi dal / transazione suddivisa sessione. Sessioni e transazioni hanno bisogno di diverse vite. Nel mondo desktop, una sessione di solito è "per-screen" e di una transazione è "per utente-azione".

Maggiori informazioni contenute in questo eccellente articolo .

Quindi quello che ho finito con era:

  • IUnitOfWork -> Wraps sessione, implementa IDisposable
  • IAtomicUnitOfWork -> Wraps transazione, implementa IDisposable
  • IRepository -> Fornisce Get, Salva, l'accesso Elimina e di query

Ho fatto in modo che avete bisogno di un IUnitOfWork di costruire un IAtomicUnitOfWork e avete bisogno di un IAtomicUnitOfWork per costruire un IRepository, in modo che impone una corretta gestione delle transazioni. Questo è davvero tutto quello che ho guadagnato implementando le mie proprie interfacce.

Come jeroenh detto, si è quasi altrettanto bene per l'uso ISession e ITransaction ma alla fine mi sentivo un po 'meglio scrivere tutto il mio codice contro un'interfaccia che ho definito.

Una parte importante della risposta sta in quali desideri siano le dimensioni delle tue transazioni.In questo momento (come ha indicato jeroenh) la transazione avviene per invocazione del metodo sul tuo repository.Questo è molto piccolo e probabilmente non necessario.Ho creato un'applicazione ASP.MVC e utilizza una dimensione di transazione che includeva tutto, da una singola richiesta http.Potrebbero trattarsi di più letture/aggiornamenti del database.Sto utilizzando la stessa unità di lavoro e Ninject per IOC.Dai un'occhiata, forse qualcosa ti aiuterà a risolvere i tuoi problemi:

http://bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/

http://bobcravens.com/2010/07/using-nhibernate-in-asp-net-mvc/

http://bobcravens.com/2010/09/the-repository-pattern-part-2/

http://bobcravens.com/2010/11/using-ninject-to-manage-critical-resources/

Spero che questo ti aiuti.

Bob

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top