// Copyright (C) Stichting Deltares 2016. All rights reserved. // // This file is part of Ringtoets. // // Ringtoets is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.IO; using System.Linq; using Application.Ringtoets.Storage.DbContext; using Application.Ringtoets.Storage.Exceptions; using Application.Ringtoets.Storage.Persistors; using Application.Ringtoets.Storage.Properties; using Core.Common.Base.Data; using Core.Common.Base.Storage; using Core.Common.Utils; using Core.Common.Utils.Builders; using UtilsResources = Core.Common.Utils.Properties.Resources; namespace Application.Ringtoets.Storage { /// /// This class interacts with an SQLite database file using the Entity Framework. /// public class StorageSqLite : IStoreProject { private string filePath; private string connectionString; /// /// Converts to a new in the database. /// /// Path to database file. /// to save. /// Returns the number of changes that were saved in . /// Thrown when is null. /// is invalid. /// Thrown when /// /// The database does not contain the table version /// THe file was not created. /// Saving the to the database failed. /// The connection to the database file failed. /// /// public int SaveProjectAs(string databaseFilePath, Project project) { SetConnectionToNewFile(databaseFilePath); using (var dbContext = new RingtoetsEntities(connectionString)) { var projectEntityPersistor = new ProjectEntityPersistor(dbContext); try { projectEntityPersistor.InsertModel(project); var changes = dbContext.SaveChanges(); projectEntityPersistor.PerformPostSaveActions(); project.Name = Path.GetFileNameWithoutExtension(databaseFilePath); return changes; } catch (DataException exception) { throw CreateStorageWriterException(Resources.Error_Update_Database, exception); } catch (SystemException exception) { if (exception is InvalidOperationException || exception is NotSupportedException) { throw CreateStorageWriterException(Resources.Error_During_Connection, exception); } throw; } } } /// /// Converts to an existing in the database. /// /// Path to database file. /// to save. /// Returns the number of changes that were saved in . /// Thrown when is null. /// is invalid. /// Thrown when /// /// does not exist. /// The database does not contain the table version. /// Saving the to the database failed. /// The connection to the database file failed. /// The related entity was not found in the database. Therefore, no update was possible. /// /// public int SaveProject(string databaseFilePath, Project project) { SetConnectionToFile(databaseFilePath); if (project == null) { throw new ArgumentNullException("project"); } using (var dbContext = new RingtoetsEntities(connectionString)) { var projectEntityPersistor = new ProjectEntityPersistor(dbContext); try { ICollection projectEntities = dbContext.ProjectEntities.ToList(); projectEntityPersistor.UpdateModel(project); projectEntityPersistor.RemoveUnModifiedEntries(projectEntities); var changes = dbContext.SaveChanges(); projectEntityPersistor.PerformPostSaveActions(); return changes; } catch (EntityNotFoundException) { throw CreateStorageWriterException(string.Format(Resources.Error_Entity_Not_Found_0_1, "project", project.StorageId)); } catch (DataException exception) { throw CreateStorageWriterException(Resources.Error_Update_Database, exception); } catch (SystemException exception) { if (exception is InvalidOperationException || exception is NotSupportedException) { throw CreateStorageWriterException(Resources.Error_During_Connection, exception); } throw; } } } /// /// Attempts to load the from the SQLite database. /// /// Path to database file. /// Returns a new instance of with the data from the database or null when not found. /// is invalid. /// Thrown when /// /// does not exist. /// The database does not contain all requested tables. /// The connection to the database file failed. /// The related entity was not found in the database. /// /// public Project LoadProject(string databaseFilePath) { SetConnectionToFile(databaseFilePath); try { using (var dbContext = new RingtoetsEntities(connectionString)) { var projectEntityPersistor = new ProjectEntityPersistor(dbContext); var project = projectEntityPersistor.GetEntityAsModel(); project.Name = Path.GetFileNameWithoutExtension(databaseFilePath); return project; } } catch (DataException exception) { throw CreateStorageReaderException(string.Empty, exception); } catch (InvalidOperationException exception) { throw CreateStorageReaderException(string.Empty, exception); } } /// /// Throws a configured instance of when writing to the storage file failed. /// /// The critical error message. /// Optional: exception that caused this exception to be thrown. /// Returns a new . private StorageException CreateStorageWriterException(string errorMessage, Exception innerException = null) { var message = new FileWriterErrorMessageBuilder(filePath).Build(errorMessage); return new StorageException(message, innerException); } /// /// Throws a configured instance of when reading the storage file failed. /// /// The critical error message. /// Optional: exception that caused this exception to be thrown. /// Returns a new . private StorageException CreateStorageReaderException(string errorMessage, Exception innerException = null) { var message = new FileReaderErrorMessageBuilder(filePath).Build(errorMessage); return new StorageException(message, innerException); } /// /// Attempts to set the connection to an existing storage file . /// /// Path to database file. /// is invalid. /// Thrown when: /// does not exist /// the database does not contain the table version. /// /// private void SetConnectionToFile(string databaseFilePath) { FileUtils.ValidateFilePath(databaseFilePath); filePath = databaseFilePath; if (!File.Exists(databaseFilePath)) { throw CreateStorageReaderException(string.Empty, new CouldNotConnectException(UtilsResources.Error_File_does_not_exist)); } connectionString = SqLiteConnectionStringBuilder.BuildSqLiteEntityConnectionString(databaseFilePath); ValidateStorage(); } /// /// Sets the connection to a newly created (empty) Ringtoets database file. /// /// Path to database file. /// is invalid. /// Thrown when: /// was not created /// executing DatabaseStructure script failed /// the database does not contain the table version. /// /// private void SetConnectionToNewFile(string databaseFilePath) { StorageSqliteCreator.CreateDatabaseStructure(databaseFilePath); SetConnectionToFile(databaseFilePath); } /// /// Validates if the connected storage is a valid Ringtoets database. /// /// Thrown when the database does not contain the table version. private void ValidateStorage() { using (var dbContext = new RingtoetsEntities(connectionString)) { try { dbContext.Database.Initialize(true); dbContext.Versions.Load(); } catch { throw CreateStorageReaderException(string.Empty, new StorageValidationException(string.Format(Resources.Error_Validating_Database_0, filePath))); } } } } }