1
\$\begingroup\$

I'm trying to figure out how to create a NHibernate UnitOfWork using .NET Core Dependency Injection. I was just hard-coding the connection string before moving it to appsettings.json configuration file. Is this form of creating a UOW in the service.scope valid?

It works, but I'm not sure if this is the intended way to do it.

Startup.cs

services.AddScoped<IUnitOfWork, UnitOfWorkV>( x=> { return new UnitOfWorkV(Configuration); });

UnitOfWorkV.cs

public UnitOfWorkV(IConfiguration configuration)
        {
            _sessionFactory = Fluently.Configure()
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(configuration.GetSection("ConnectionStrings:ConexionV").Value))
                .ExposeConfiguration(ConfigureNhibernateValidator)
                .Mappings(m =>
                {
                    m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
                    m.HbmMappings.AddFromAssemblyOf<JobQueue>();
                })
                .BuildSessionFactory();
            Session = _sessionFactory.OpenSession();
            Session.FlushMode = FlushMode.Auto;
        }

appsettings.json

{
  "ConnectionStrings": {
    "ConexionV": "Some connection string"
  }
}
\$\endgroup\$
1
\$\begingroup\$

In some camps it is not advised to tightly couple your code to framework specific dependencies like IConfiguration.

Based on the shown constructor it looks like the UnitOfWorkV really needs the connection string and not the configuration. That late bound constructor dependency will only be realized when the class is being resolved.

Refactor the class to depend on what is actually needed.

public UnitOfWorkV(string connectionString) {
    _sessionFactory = Fluently.Configure()
        .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
        .ExposeConfiguration(ConfigureNhibernateValidator)
        .Mappings(m => {
            m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
            m.HbmMappings.AddFromAssemblyOf<JobQueue>();
        })
        .BuildSessionFactory();
    Session = _sessionFactory.OpenSession();
    Session.FlushMode = FlushMode.Auto;
}

And get the connection string at start up,

var connectionString = Configuration.GetConnectionString("ConexionV");

injecting it explicitly into the class.

if(connectionString == null) //Fail early
    throw new Exception("Missing connection string");

services.AddScoped<IUnitOfWork, UnitOfWorkV>(_ => new UnitOfWorkV(connectionString));

That way, any validation can be done immediately at startup instead of deferring to when resolving the actually class

\$\endgroup\$
0
\$\begingroup\$

After encountering a huge memory leak (14GB of memory usage after 24 hrs of new ISession, each one created once the WS was called) with this approach we came to an extended answer to this, the thing is this is due to our UnitOfWork and Repository implementation we used to open the session once the object was created, with this new approach we rely on a singleton IUnitOfWork and open the session by request.

Startup.cs

 services.AddSingleton<IUnitOfWork, UnitOfWorkV>(x => { return new UnitOfWorkV(Configuration.GetSection("ConnectionStrings:ConexionV").Value); });

We changed .AddScoped() to .AddSingleton()

UnitOfWorkV.cs

public UnitOfWorkVentas(string conexion)
        {
            // SE CREA EL SESSION FACTORY DE LA UNIDAD DE TRABAJO...
            _sessionFactory = Fluently.Configure()
                //Obtenemos la cadena de conexion del IConfiguration
                .Database(MsSqlConfiguration.MsSql2008.ConnectionString(conexion))
                .ExposeConfiguration(ConfigureNhibernateValidator)
                .Mappings(m =>
                {
                    m.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
                    m.HbmMappings.AddFromAssemblyOf<JobQueue>();
                })
                .BuildSessionFactory();
            //Creamos la Session

        }

Create a session from a connection string received in the constructor (Thanks for the recommendation of @Nkosi ).

public void OpenSession()
{
    Session = _sessionFactory.OpenSession();
    Session.FlushMode = FlushMode.Auto;
}

Create an OpenSession() method in our UnitOfWork class

RepositoryV.cs

public RepositoryVentas(IUnitOfWork unitOfWork)
{
    _unitOfWork = (UnitOfWorkVentas)unitOfWork;
    _unitOfWork.AbrirSesion();
}

Open the Session in the Repository constructor

\$\endgroup\$
  • \$\begingroup\$ I wouldnt make UnitOfWork a Singleton, if anything that could cause memory leaks.. UOW is meant to be short lived, I cant see what type of project this is - WebApi? .. If so, definately should be Scoped or Transient depending on how fine grained your methods are.. \$\endgroup\$ – Robert Perry Mar 20 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.