/// /// Copyright (c) 2013-2016 Sensus Metering Systems /// using System; using System.Collections.Generic; using FluentNHibernate.Cfg; using FluentNHibernate.Cfg.Db; using log4net; using NHibernate; using NHibernate.Cfg; using NHibernate.Tool.hbm2ddl; using Results.Entities; namespace Results { public static class DB { static readonly ILog log = LogManager.GetLogger(typeof(DB)); /// Session factory for all regular sessions, not for CreateEmptyResultsDB(). public static ISessionFactory SessionFactory; /// Connection string for all sessions static string connectionString; /// public static string ConnectionString { get { return connectionString; } set { if (value != connectionString) { connectionString = value; SessionFactory = null; /// Clear SessionFactory on connection string change } } } /// Database type (MySQL or SQLite) for all sessions private static Common.DBType dbType; /// public static Common.DBType DbType { get { return dbType; } set { dbType = value; SessionFactory = null; } } /// /// NHibernate session factory (to create the database session 'SessionFactory') /// /// A database session static ISessionFactory CreateSessionFactory() { return CreateSessionFactory(false); } /// /// NHibernate session factory (to create the database session 'SessionFactory') /// /// true = Create a new DB, false = Regular DB /// A database session public static ISessionFactory CreateSessionFactory(bool createDB) { FluentConfiguration cfg = Fluently.Configure(); switch (dbType) { default: case Common.DBType.MySql: cfg = cfg.Database(MySQLConfiguration.Standard.ConnectionString(connectionString)); break; case Common.DBType.SQLite: cfg = cfg.Database(SQLiteConfiguration.Standard.UsingFile(connectionString)); break; } cfg = cfg.Mappings(m => m.FluentMappings.AddFromAssemblyOf()); if (createDB) { return cfg.ExposeConfiguration(BuildSchemaCreate) .BuildConfiguration() .SetProperty("hibernate.connection.connect_timeout", Common.Const.MySqlConnectTimeoutSec) .BuildSessionFactory(); } else { return cfg.ExposeConfiguration(BuildSchema) .BuildConfiguration() .SetProperty("hibernate.connection.connect_timeout", Common.Const.MySqlConnectTimeoutSec) .BuildSessionFactory(); } } static void BuildSchema(Configuration config) { /// This NHibernate tool takes a configuration with mapping info and exports a database schema new SchemaExport(config).SetOutputFile("db_schema"); } static void BuildSchemaCreate(Configuration config) { /// This NHibernate tool takes a configuration with mapping info and exports a database schema new SchemaExport(config).Create(true, true); } /// Create a NHibernate session for the given database public static ISession CreateSession() { if (string.IsNullOrEmpty(connectionString)) { throw new Exception("Connection string was not specified"); } if (SessionFactory == null) SessionFactory = CreateSessionFactory(); return SessionFactory.OpenSession(); } public static void SaveObject(object obj) { SaveObject(CreateSession(), obj); } /// public static void SaveObject(ISession session, object obj) { using (var transaction = session.BeginTransaction()) { session.SaveOrUpdate(obj); try { transaction.Commit(); } catch { } } } public static void DeleteObject(object obj) { DeleteObject(CreateSession(), obj); } /// public static void DeleteObject(ISession session, object obj) { using (var transaction = session.BeginTransaction()) { session.Delete(obj); transaction.Commit(); } } /// /// Create an empty users database. /// Database contains only the user 'admin' and the control board component 'CB'. /// /// DBType.SQLite or DBType.MySql /// Connection string /// true=success, false=error public static bool CreateEmptyDB() { ISessionFactory sessionFactory = CreateSessionFactory(true); if (sessionFactory == null) return false; /// Populate the database using (var session = sessionFactory.OpenSession()) { using (var transaction = session.BeginTransaction()) { transaction.Commit(); } } return true; } /// /// Shared data /// public static IList TestDataList; public static IList ComponentsList; public static IList WaterMeterDataList; /// /// Loads shared data from the database /// /// Throws NHibernate exceptions public static void LoadSharedData(ISession session = null) { bool openAndCloseSession = (session == null); try { if (openAndCloseSession) session = Results.DB.CreateSession(); TestDataList = session.QueryOver().List(); ComponentsList = session.QueryOver().List(); WaterMeterDataList = session.QueryOver().List(); } catch (Exception e) { log.ErrorFormat("Cannot open results DB: {0}", e.Message); } finally { if (openAndCloseSession && session != null && session.IsOpen) session.Close(); } } /// /// Loads shared data from the database /// /// Throws NHibernate exceptions public static int GetMaxSavedBatchNr(ISession session = null) { bool openAndCloseSession = (session == null); int maxBatchNr = 0; try { if (openAndCloseSession) session = Results.DB.CreateSession(); IList batches = session.QueryOver().List(); foreach (var b in batches) { if (b.BatchNr > maxBatchNr) maxBatchNr = b.BatchNr; } } catch (Exception e) { log.ErrorFormat("Cannot open results DB: {0}", e.Message); } finally { if (openAndCloseSession && session != null && session.IsOpen) session.Close(); } return maxBatchNr; } /// /// Update TestData, Components and WaterMeterData fo tests and water meters in batch results /// with existing data in static lists TestDataList, ComponentsList and WaterMeterDataList. /// /// Batch results /// true when any data modiifed public static bool UpdateBatchData(Entities.Batch batch) { ISession session = DB.CreateSession(); if (session == null) return false; foreach (var tr in batch.TestRslts) { /// Search in TestDataList and update test data in the new batch TestData td = TestData.UpdateList(TestDataList, tr.TestData); tr.TestData = td; /// Search in ComponentsList and update components in the new batch Components cd = Components.UpdateList(ComponentsList, tr.Components); tr.Components = cd; } foreach (var wm in batch.WaterMeters) { /// Search in WaterMeterDataList and update water meter data in the new batch WaterMeterData wmd = WaterMeterData.UpdateList(WaterMeterDataList, wm.WaterMeterData); wm.WaterMeterData = wmd; } return true; /// TODO: Really check for modifications } public static bool SaveNewBatch(Entities.Batch batch) { ISession session = DB.CreateSession(); if (session == null) return false; TestDataList = session.QueryOver().List(); ComponentsList = session.QueryOver().List(); WaterMeterDataList = session.QueryOver().List(); UpdateBatchData(batch); ITransaction transaction = session.BeginTransaction(); if (transaction == null) return false; try { foreach (var td in TestDataList) if (td.Id == 0) session.SaveOrUpdate(td); foreach (var cd in ComponentsList) if (cd.Id == 0) session.SaveOrUpdate(cd); foreach (var wd in WaterMeterDataList) if (wd.Id == 0) session.SaveOrUpdate(wd); log.Debug(batch.ToString(1)); foreach (var tstRslt in batch.TestRslts) { log.Debug(tstRslt.ToString(1)); } session.SaveOrUpdate(batch); transaction.Commit(); } catch (Exception exc) { transaction.Rollback(); log.FatalFormat("Saving results of batch #{0} into the results DB failed: {1}", batch.BatchNr, exc.Message); if (exc.InnerException != null && !string.IsNullOrEmpty(exc.InnerException.Message)) { log.FatalFormat("Inner exception message: {0}", exc.InnerException.Message); } return false; } session.Flush(); return true; } public static Batch LoadBatch(int batchNr) { IList batches; ISession session = DB.CreateSession(); if (session == null) return null; try { TestDataList = session.QueryOver().List(); ComponentsList = session.QueryOver().List(); WaterMeterDataList = session.QueryOver().List(); batches = session.QueryOver() .Where(x => (x.BatchNr == batchNr)) .List(); foreach (var batch in batches) { batch.TestRslts = session.QueryOver() .Where(x => (x.Batch.Id == batch.Id)) .List(); batch.WaterMeters = session.QueryOver() .Where(x => (x.Batch.Id == batch.Id)) .List(); } return (batches.Count > 0) ? batches[0] : null; } catch (Exception exc) { log.FatalFormat("Cannot load batch #{0}: {1}", batchNr, exc.Message); return null; } } } }