NHibernate SessionFactory Configuration for In‑Memory SQLite Tests Without Production Credentials
0 reputation · 21 Oct 2020, 19:19 UTC
0 reputation · 21 Oct 2020, 19:19 UTC
Goal: configure NHibernate integration tests to run against an in‑memory SQLite database so that no production credentials are required, while guaranteeing that any mistake in the connection string is caught before the test suite starts.
Uncertainty: NHibernate reads the connection string from the configuration but does not open a physical connection until the first ISession is created; the SessionFactory construction may succeed even with an invalid string, deferring the error to runtime when a session is opened. This lazy connection resolution makes early‑failure detection in test harnesses unreliable.
34775 reputation · 22 Oct 2020, 02:38 UTC
NHibernate does not validate the connection string or open a physical database connection during ISessionFactory.BuildSessionFactory(). The factory is built successfully even with an invalid string; a connection is opened lazily when the first ISession is created or when schema export runs. Therefore an incorrect connection string is only discovered at runtime, not at factory build time.
NHibernate.Driver.SQLite20Driver and NHibernate.Dialect.SQLiteDialect. The SessionFactory is thread‑safe and expensive to create.cache=shared is used or a single ADO.NET connection is kept open for the fixture.SchemaExport (or SchemaUpdate) opens a connection and will fail fast if the connection string is wrong, providing an early‑validation point.Because the connection is opened lazily, tests that rely on the factory build succeeding may still encounter empty tables or connection errors when the first session is opened. Keeping one open ADO.NET connection for the fixture lifetime and reusing it for schema export and sessions makes the in‑memory database visible to all sessions in that fixture.
var cfg = new NHibernate.Cfg.Configuration()
.DataBaseIntegration(db => {
db.Dialect();
db.Driver();
db.ConnectionString = \"Data Source=:memory:;Version=3;New=True;\";
db.ConnectionReleaseMode = NHibernate.Connection.ConnectionReleaseMode.OnClose;
db.IsolationLevel = System.Data.IsolationLevel.ReadCommitted;
})
.AddAssembly(typeof(YourEntity).Assembly);
var factory = cfg.BuildSessionFactory();
// Option A: test connection
using var testSession = factory.OpenSession();
testSession.CreateSQLQuery(\"SELECT 1\").UniqueResult();
// Option B: schema export
new NHibernate.Tool.hbm2ddl.SchemaExport(cfg).Create(false, true);
using var schemaSession = factory.OpenSession();
using var tx = schemaSession.BeginTransaction();
// schema already exported; tx can be committed or rolled back as needed
tx.Commit();
hibernate.cfg.xml or app settings are referenced in the test build.:memory: and no production host or credentials.One diagnostic detail that changes the recommendation: which SQLite ADO.NET provider you are using (e.g., System.Data.SQLite vs Microsoft.Data.Sqlite) because the connection string format and driver class differ, affecting the exact keep‑alive pattern for :memory:.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 22 Oct 2020, 00:01 UTC
After BuildSessionFactory() you can immediately open a session to trigger the provider’s connection parsing. Combine this with db.ConnectionReleaseMode = ConnectionReleaseMode.OnClose so the single connection stays alive for the entire fixture, keeping the in‑memory database alive across tests.
var cfg = new Configuration();
cfg.DataBaseIntegration(db => {
db.Dialect<SQLiteDialect>();
db.Driver<SQLite20Driver>();
db.ConnectionString = "Data Source=:memory:;Cache=Shared;";
db.ConnectionReleaseMode = ConnectionReleaseMode.OnClose;
});
var sessionFactory = cfg.BuildSessionFactory();
// Force validation
using var session = sessionFactory.OpenSession();
// Optionally run SchemaExport to create tables
new SchemaExport(cfg).Create(false, true);
Opening the session first ensures any typo in the connection string surfaces instantly. The shared cache keeps the database persistent for the rest of the test run, avoiding the “new empty DB per connection” pitfall.