2010-03-02 13 views
13

He escrito una clase NHibernateSessionFactory que contiene un Nhibernate ISessionFactory estático. Esto se usa para asegurarnos de que solo tenemos una fábrica de sesiones, y la primera vez que se llama OpenSession() creo la SessionFactory actual - las próximas veces que use el mismo y abra una sesión en él. El código se ve así:Asegúrese de que NHibernate SessionFactory solo se haya creado una vez

public class NhibernateSessionFactory : INhibernateSessionFactory 
{ 
    private static ISessionFactory _sessionFactory; 

    public ISession OpenSession() 
    { 
     if (_sessionFactory == null) 
     { 
      var cfg = Fluently.Configure(). 
       Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")). 
       Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>()); 
      _sessionFactory = cfg.BuildSessionFactory(); 
      BuildSchema(cfg); 
     } 
     return _sessionFactory.OpenSession(); 
    } 

    private static void BuildSchema(FluentConfiguration configuration) 
    { 
     var sessionSource = new SessionSource(configuration); 
     var session = sessionSource.CreateSession(); 
     sessionSource.BuildSchema(session);    
    } 
} 

Ahora tengo un problema. Mi aplicación se divide entre cliente y servidor. El material de Nhibernate está en el lado del servidor. Al inicio, tanto mi cliente como mi servidor desean acceder a la base de datos a través de algunos servicios que utilizarán NhibernateSessionFactory. El resultado es una condición de carrera para si se crea _sessionFactory antes de que la solicitud provenga del cliente. Si no es así, fallará ...

Supongo que necesito algún tipo de mecanismo de cola o espera en la NhibernateSessionFactory, pero no estoy seguro de qué hacer. ¿Alguien tuvo el mismo problema antes? ¿Cuál es la mejor solución?

Respuesta

17

El sessionFactory debe ser un singleton seguro para subprocesos.

Un patrón común en Java es construir el sessionFactory en un inicializador estático. Ver HibernateUtil. Puedes hacer lo mismo en C#.

Hay otros patterns para implementar singleton, incluido el uso de bloqueo o secciones sincronizadas. Aquí hay una pequeña variante que debería resolver su problema si lo entendí correctamente.

static readonly object factorylock = new object(); 

public ISession OpenSession() 
{ 
    lock (factorylock) 
    { 
     if (_sessionFactory == null) 
     { 
      var cfg = Fluently.Configure(). 
       Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")). 
       Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>()); 
      _sessionFactory = cfg.BuildSessionFactory(); 
      BuildSchema(cfg); 
     } 
    } 
    return _sessionFactory.OpenSession(); 
} 
+0

Gracias! Suena razonable. Acabo de agregar una solución usando Mutex. Pero debería usar bloqueo en su lugar? – stiank81

+0

Pero, ¿y si necesito varias conexiones de bases de datos simultáneamente? Actualmente no sé ni otra solución alternativa para crear varias Fábricas de Sesión ... – JustAMartin

+0

¿Cómo implementar esto en los controladores? – Chazt3n

4

que resolvieron este usando un Mutex al crear la SessionFactory. ¿Esto parece razonable:

public class NhibernateSessionFactory : INhibernateSessionFactory 
{ 
    private static ISessionFactory _sessionFactory; 
    private static Mutex _mutex = new Mutex(); // <-- Added 

    public ISession OpenSession() 
    { 
     if (_sessionFactory == null) 
     { 
      _mutex.WaitOne();    // <-- Added 
      if (_sessionFactory == null) // <-- Added 
      {        // <-- Added 
       var cfg = Fluently.Configure(). 
        Database(SQLiteConfiguration.Standard.ShowSql().UsingFile("Foo.db")). 
        Mappings(m => m.FluentMappings.AddFromAssemblyOf<MappingsPersistenceModel>()); 
       _sessionFactory = cfg.BuildSessionFactory(); 
       BuildSchema(cfg); 
      }        // <-- Added 
      _mutex.ReleaseMutex();   // <-- Added 

     } 
     return _sessionFactory.OpenSession(); 
    } 

    private static void BuildSchema(FluentConfiguration configuration) 
    { 
     var sessionSource = new SessionSource(configuration); 
     var session = sessionSource.CreateSession(); 
     sessionSource.BuildSchema(session);    
    } 
} 

Parece que funciona como un encanto. Pero, ¿debería usar el bloqueo en su lugar?

+2

Hmm ... Parece el "doble bloqueo de bloqueo", que no es una práctica recomendada para implementar singleton (consulte esta página http: // www.yoda.arachsys.com/csharp/singleton.html). Además, se puede usar un bloqueo simple en lugar de un mutex. – ewernli

+0

¡Gracias! Suena como que debería ir con el bloqueo simple en su lugar. – stiank81

+0

No debe usar el bloqueo, debe usar la clase Monitor, al menos. http://msdn.microsoft.com/en-us/library/x090d6tf.aspx –

Cuestiones relacionadas