From 4d2559fe780506ec20952c3017482c3d80e84b66 Mon Sep 17 00:00:00 2001 From: Marc Sallin Date: Fri, 15 Jan 2016 14:32:28 +0100 Subject: [PATCH 01/15] #16: Refactoring and added SqliteDropCreateDatabaseWhenModelChanges. --- .../Entity/CustomHistory.cs | 12 ++ SQLite.CodeFirst.Console/FootballDbContext.cs | 53 +----- .../FootballDbInitializer.cs | 53 ++++++ .../SQLite.CodeFirst.Console.csproj | 2 + .../SqliteCreateDatabaseIfNotExists.cs | 0 .../SqliteDropCreateDatabaseAlways.cs | 4 +- ...qliteDropCreateDatabaseWhenModelChanges.cs | 173 ++++++++++++++++++ .../SqliteInitializerBase.cs | 17 +- SQLite.CodeFirst/Entities/History.cs | 57 ++++++ SQLite.CodeFirst/Entities/IHistory.cs | 12 ++ SQLite.CodeFirst/IDatabaseCreator.cs | 7 + SQLite.CodeFirst/ISqliteSqlGenerator.cs | 7 + .../ColumnStatementCollectionBuilder.cs | 0 .../Builder/CreateDatabaseStatementBuilder.cs | 0 .../Builder/CreateIndexStatementBuilder.cs | 0 .../Builder/CreateTableStatementBuilder.cs | 0 .../Builder/ForeignKeyStatementBuilder.cs | 0 .../Builder/IStatementBuilder.cs | 0 .../Builder/NameCreators/IndexNameCreator.cs | 0 .../Builder/NameCreators/SpecialChars.cs | 0 .../Builder/NameCreators/TableNameCreator.cs | 0 .../Builder/PrimaryKeyStatementBuilder.cs | 0 .../SqliteForeignKeyIndexConvention.cs | 0 .../Extensions/EntityTypeExtension.cs | 0 .../ColumnConstraintCollection.cs | 0 .../ColumnConstraint/IColumnConstraint.cs | 0 .../IColumnConstraintCollection.cs | 0 .../ColumnConstraint/MaxLengthConstraint.cs | 0 .../ColumnConstraint/NotNullConstraint.cs | 0 .../Statement/ColumnStatement.cs | 0 .../Statement/ColumnStatementCollection.cs | 0 .../Statement/CreateDatabaseStatement.cs | 0 .../Statement/CreateIndexStatement.cs | 0 .../CreateIndexStatementCollection.cs | 0 .../Statement/CreateTableStatement.cs | 0 .../Statement/ForeignKeyStatement.cs | 0 .../{ => Internal}/Statement/IStatement.cs | 0 .../Statement/IStatementCollection.cs | 0 .../Statement/PrimaryKeyStatement.cs | 0 .../Utility/AssociationTypeWrapper.cs | 0 .../Utility/ConnectionStringParser.cs} | 8 +- .../Internal/Utility/HashCreator.cs | 18 ++ .../Utility/HistoryEntityTypeValidator.cs | 19 ++ SQLite.CodeFirst/SQLite.CodeFirst.csproj | 73 ++++---- .../SQLite.CodeFirst.csproj.DotSettings | 8 + SQLite.CodeFirst/SqliteDatabaseCreator.cs | 23 +-- SQLite.CodeFirst/SqliteSqlGenerator.cs | 29 +++ 47 files changed, 464 insertions(+), 111 deletions(-) create mode 100644 SQLite.CodeFirst.Console/Entity/CustomHistory.cs create mode 100644 SQLite.CodeFirst.Console/FootballDbInitializer.cs rename SQLite.CodeFirst/{ => DbInitializers}/SqliteCreateDatabaseIfNotExists.cs (100%) rename SQLite.CodeFirst/{ => DbInitializers}/SqliteDropCreateDatabaseAlways.cs (95%) create mode 100644 SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs rename SQLite.CodeFirst/{ => DbInitializers}/SqliteInitializerBase.cs (87%) create mode 100644 SQLite.CodeFirst/Entities/History.cs create mode 100644 SQLite.CodeFirst/Entities/IHistory.cs create mode 100644 SQLite.CodeFirst/IDatabaseCreator.cs create mode 100644 SQLite.CodeFirst/ISqliteSqlGenerator.cs rename SQLite.CodeFirst/{ => Internal}/Builder/ColumnStatementCollectionBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/CreateDatabaseStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/CreateIndexStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/CreateTableStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/ForeignKeyStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/IStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/NameCreators/IndexNameCreator.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/NameCreators/SpecialChars.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/NameCreators/TableNameCreator.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Builder/PrimaryKeyStatementBuilder.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Convention/SqliteForeignKeyIndexConvention.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Extensions/EntityTypeExtension.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnConstraint/ColumnConstraintCollection.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnConstraint/IColumnConstraint.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnConstraint/IColumnConstraintCollection.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnConstraint/MaxLengthConstraint.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnConstraint/NotNullConstraint.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ColumnStatementCollection.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/CreateDatabaseStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/CreateIndexStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/CreateIndexStatementCollection.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/CreateTableStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/ForeignKeyStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/IStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/IStatementCollection.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Statement/PrimaryKeyStatement.cs (100%) rename SQLite.CodeFirst/{ => Internal}/Utility/AssociationTypeWrapper.cs (100%) rename SQLite.CodeFirst/{SqliteConnectionStringParser.cs => Internal/Utility/ConnectionStringParser.cs} (91%) create mode 100644 SQLite.CodeFirst/Internal/Utility/HashCreator.cs create mode 100644 SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs create mode 100644 SQLite.CodeFirst/SQLite.CodeFirst.csproj.DotSettings create mode 100644 SQLite.CodeFirst/SqliteSqlGenerator.cs diff --git a/SQLite.CodeFirst.Console/Entity/CustomHistory.cs b/SQLite.CodeFirst.Console/Entity/CustomHistory.cs new file mode 100644 index 0000000..51ad92c --- /dev/null +++ b/SQLite.CodeFirst.Console/Entity/CustomHistory.cs @@ -0,0 +1,12 @@ +using System; + +namespace SQLite.CodeFirst.Console.Entity +{ + public class CustomHistory : IHistory + { + public int Id { get; set; } + public string Hash { get; set; } + public string Context { get; set; } + public DateTime CreateDate { get; set; } + } +} diff --git a/SQLite.CodeFirst.Console/FootballDbContext.cs b/SQLite.CodeFirst.Console/FootballDbContext.cs index 6f0ed9e..e3eabcf 100644 --- a/SQLite.CodeFirst.Console/FootballDbContext.cs +++ b/SQLite.CodeFirst.Console/FootballDbContext.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.ModelConfiguration.Conventions; +using System.Data.Entity; using SQLite.CodeFirst.Console.Entity; namespace SQLite.CodeFirst.Console @@ -16,8 +14,6 @@ public FootballDbContext() protected override void OnModelCreating(DbModelBuilder modelBuilder) { - modelBuilder.Conventions.Remove(); - ConfigureTeamEntity(modelBuilder); ConfigureStadionEntity(modelBuilder); ConfigureCoachEntity(modelBuilder); @@ -61,51 +57,4 @@ private static void ConfigurePlayerEntity(DbModelBuilder modelBuilder) .WillCascadeOnDelete(true); } } - - public class FootballDbInitializer : SqliteDropCreateDatabaseAlways - { - public FootballDbInitializer(DbModelBuilder modelBuilder) - : base(modelBuilder) - { } - - protected override void Seed(FootballDbContext context) - { - context.Set().Add(new Team - { - Name = "YB", - Coach = new Coach - { - City = "Zürich", - FirstName = "Masssaman", - LastName = "Nachn", - Street = "Testingstreet 844" - }, - Players = new List - { - new Player - { - City = "Bern", - FirstName = "Marco", - LastName = "Bürki", - Street = "Wunderstrasse 43", - Number = 12 - }, - new Player - { - City = "Berlin", - FirstName = "Alain", - LastName = "Rochat", - Street = "Wonderstreet 13", - Number = 14 - } - }, - Stadion = new Stadion - { - Name = "Stade de Suisse", - City = "Bern", - Street = "Papiermühlestrasse 71" - } - }); - } - } } diff --git a/SQLite.CodeFirst.Console/FootballDbInitializer.cs b/SQLite.CodeFirst.Console/FootballDbInitializer.cs new file mode 100644 index 0000000..8aa6199 --- /dev/null +++ b/SQLite.CodeFirst.Console/FootballDbInitializer.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Data.Entity; +using SQLite.CodeFirst.Console.Entity; + +namespace SQLite.CodeFirst.Console +{ + public class FootballDbInitializer : SqliteDropCreateDatabaseWhenModelChanges + { + public FootballDbInitializer(DbModelBuilder modelBuilder) + : base(modelBuilder, typeof(CustomHistory)) + { } + + protected override void Seed(FootballDbContext context) + { + context.Set().Add(new Team + { + Name = "YB", + Coach = new Coach + { + City = "Zürich", + FirstName = "Masssaman", + LastName = "Nachn", + Street = "Testingstreet 844" + }, + Players = new List + { + new Player + { + City = "Bern", + FirstName = "Marco", + LastName = "Bürki", + Street = "Wunderstrasse 43", + Number = 12 + }, + new Player + { + City = "Berlin", + FirstName = "Alain", + LastName = "Rochat", + Street = "Wonderstreet 13", + Number = 14 + } + }, + Stadion = new Stadion + { + Name = "Stade de Suisse", + City = "Bern", + Street = "Papiermühlestrasse 71" + } + }); + } + } +} \ No newline at end of file diff --git a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj index e91b289..4c517f7 100644 --- a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj +++ b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj @@ -77,12 +77,14 @@ Properties\AssemblyVersionInfo.cs + + diff --git a/SQLite.CodeFirst/SqliteCreateDatabaseIfNotExists.cs b/SQLite.CodeFirst/DbInitializers/SqliteCreateDatabaseIfNotExists.cs similarity index 100% rename from SQLite.CodeFirst/SqliteCreateDatabaseIfNotExists.cs rename to SQLite.CodeFirst/DbInitializers/SqliteCreateDatabaseIfNotExists.cs diff --git a/SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs b/SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseAlways.cs similarity index 95% rename from SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs rename to SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseAlways.cs index 91a8c19..b022345 100644 --- a/SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs +++ b/SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseAlways.cs @@ -30,8 +30,8 @@ public override void InitializeDatabase(TContext context) { string databseFilePath = GetDatabasePathFromContext(context); - bool dbExists = File.Exists(databseFilePath); - if (dbExists) + bool exists = File.Exists(databseFilePath); + if (exists) { File.Delete(databseFilePath); } diff --git a/SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs b/SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs new file mode 100644 index 0000000..a0672fe --- /dev/null +++ b/SQLite.CodeFirst/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs @@ -0,0 +1,173 @@ +using System; +using System.Data.Common; +using System.Data.Entity; +using System.Diagnostics; +using System.IO; +using System.Linq; +using SQLite.CodeFirst.Utility; + +namespace SQLite.CodeFirst +{ + /// + /// An implementation of that will always recreate and optionally re-seed the + /// database the first time that a context is used in the app domain or if the model has changed. + /// To seed the database, create a derived class and override the Seed method. + /// + /// To detect model changes a new table (implementation of ) is added to the database. + /// There is one record in this table which holds the hash of the SQL-statement which was generated from the model + /// executed to create the database. When initializing the database the initializer checks if the hash of the SQL-statement for the + /// model is still the same as the hash in the database. If you use this initializer on a existing database, this initializer + /// will interpret this as model change because of the new table. + /// Notice that a database can be used by more than one context. Therefore the name of the context is saved as a part of the history record. + /// + /// + /// The type of the context. + public class SqliteDropCreateDatabaseWhenModelChanges : SqliteInitializerBase + where TContext : DbContext + { + private readonly Type historyEntityType; + + /// + /// Initializes a new instance of the class. + /// + /// The model builder. + public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder) + : base(modelBuilder) + { + historyEntityType = typeof(History); + ConfigureHistoryEntity(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The model builder. + /// Type of the history entity (must implement and provide an parameterless constructor). + public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder, Type historyEntityType) + : base(modelBuilder) + { + this.historyEntityType = historyEntityType; + ConfigureHistoryEntity(); + } + + + protected void ConfigureHistoryEntity() + { + HistoryEntityTypeValidator.EnsureValidType(historyEntityType); + ModelBuilder.RegisterEntityType(historyEntityType); + } + + /// + /// Initialize the database for the given context. + /// Generates the SQLite-DDL from the model and executs it against the database. + /// After that the method is executed. + /// All actions are be executed in transactions. + /// + /// The context. + public override void InitializeDatabase(TContext context) + { + string databseFilePath = GetDatabasePathFromContext(context); + + bool dbExists = File.Exists(databseFilePath); + if (dbExists) + { + if (IsSameModel(context)) + { + return; + } + + DeleteDatabase(context, databseFilePath); + base.InitializeDatabase(context); + SaveHistory(context); + } + else + { + base.InitializeDatabase(context); + SaveHistory(context); + } + } + + private void DeleteDatabase(TContext context, string databseFilePath) + { + context.Database.Connection.Close(); + GC.Collect(); + + // TODO: Comment + for (int i = 0; i < 5; i++) + { + try + { + File.Delete(databseFilePath); + // TODO: Throw exception if 5 tries.. + break; + } + catch (Exception) + { + System.Threading.Thread.Sleep(1); + } + } + } + + private void SaveHistory(TContext context) + { + var hash = GetHashFromModel(context.Database.Connection); + var history = GetHistoryRecord(context); + EntityState entityState; + if (history == null) + { + history = (IHistory)Activator.CreateInstance(historyEntityType); + entityState = EntityState.Added; + } + else + { + entityState = EntityState.Modified; + } + + history.Context = context.GetType().FullName; + history.Hash = hash; + history.CreateDate = DateTime.UtcNow; + + context.Set(historyEntityType).Attach(history); + context.Entry(history).State = entityState; + context.SaveChanges(); + } + + private bool IsSameModel(TContext context) + { + try + { + var hash = GetHashFromModel(context.Database.Connection); + var history = GetHistoryRecord(context); + return history?.Hash == hash; + } + catch (Exception) + { + return false; + } + } + + private IHistory GetHistoryRecord(TContext context) + { + return context.Set(historyEntityType) + .AsNoTracking() + .ToListAsync() + .Result + .Cast() + .SingleOrDefault(); + } + + private string GetHashFromModel(DbConnection connection) + { + var sql = GetSqlFromModel(connection); + string hash = HashCreator.CreateHash(sql); + return hash; + } + + private string GetSqlFromModel(DbConnection connection) + { + var model = ModelBuilder.Build(connection); + var sqliteSqlGenerator = new SqliteSqlGenerator(model.StoreModel); + return sqliteSqlGenerator.Generate(); + } + } +} diff --git a/SQLite.CodeFirst/SqliteInitializerBase.cs b/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs similarity index 87% rename from SQLite.CodeFirst/SqliteInitializerBase.cs rename to SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs index 25b571f..0f13684 100644 --- a/SQLite.CodeFirst/SqliteInitializerBase.cs +++ b/SQLite.CodeFirst/DbInitializers/SqliteInitializerBase.cs @@ -3,6 +3,7 @@ using System.Data.Entity.ModelConfiguration.Conventions; using SQLite.CodeFirst.Convention; using System.IO; +using SQLite.CodeFirst.Utility; namespace SQLite.CodeFirst { @@ -10,17 +11,17 @@ namespace SQLite.CodeFirst /// An basic implementation of the interface. /// This class provides common logic which can be used when writing an Sqlite-Initializer. /// The logic provided is: - /// 1. Remove/Add specific Conventions + /// 1. Remove/Add specific conventions /// 2. Get the path to the database file - /// 3. Create a new SQLite-Database from the model (Code First) + /// 3. Create a new SQLite-database from the model (Code First) /// 4. Seed data to the new created database - /// The following implementations are provided: , . + /// The following implementations are provided: , . /// /// The type of the context. public abstract class SqliteInitializerBase : IDatabaseInitializer where TContext : DbContext { - private readonly DbModelBuilder modelBuilder; + protected readonly DbModelBuilder ModelBuilder; protected SqliteInitializerBase(DbModelBuilder modelBuilder) { @@ -29,7 +30,7 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder) throw new ArgumentNullException("modelBuilder"); } - this.modelBuilder = modelBuilder; + ModelBuilder = modelBuilder; // This convention will crash the SQLite Provider before "InitializeDatabase" gets called. // See https://github.com/msallin/SQLiteCodeFirst/issues/7 for details. @@ -44,7 +45,7 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder) // The own convention will rename the automatically created indicies by using the correct scheme. modelBuilder.Conventions.AddAfter(new SqliteForeignKeyIndexConvention()); } - catch (InvalidOperationException exception) + catch (InvalidOperationException) { // Ignore it. } @@ -59,7 +60,7 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder) /// The context. public virtual void InitializeDatabase(TContext context) { - var model = modelBuilder.Build(context.Database.Connection); + var model = ModelBuilder.Build(context.Database.Connection); var dbFile = GetDatabasePathFromContext(context); var dbFileInfo = new FileInfo(dbFile); @@ -110,7 +111,7 @@ protected virtual void Seed(TContext context) { } /// The full path to the SQLite database file. protected string GetDatabasePathFromContext(TContext context) { - return SqliteConnectionStringParser.GetDataSource(context.Database.Connection.ConnectionString); + return ConnectionStringParser.GetDataSource(context.Database.Connection.ConnectionString); } } } \ No newline at end of file diff --git a/SQLite.CodeFirst/Entities/History.cs b/SQLite.CodeFirst/Entities/History.cs new file mode 100644 index 0000000..f015639 --- /dev/null +++ b/SQLite.CodeFirst/Entities/History.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace SQLite.CodeFirst +{ + /// + /// Represents the database table "history" which is used to store the necessary information + /// to detect if the model has changed. + /// + [Table(nameof(History) + "_" + TableNamePostfix)] + public class History : IHistory + { + /// + /// The postfix which is appended to the history table name + /// to ensure that this table name is unique. + /// + [NotMapped] + public const string TableNamePostfix = "82c009b41631-48579635f1ff64eb62d9"; + + /// + /// Gets or sets the identifier of the record. + /// Is automatilcally generated by the database. + /// + /// + /// The identifier. + /// + [Key] + public int Id { get; set; } + + /// + /// Gets or sets the hash which represents the current database structure/state. + /// + /// + /// The hash. + /// + [Required] + public string Hash { get; set; } + + /// + /// Gets or sets the key of the DbContext to which this record belongs. + /// + /// + /// The context key. + /// + public string Context { get; set; } + + /// + /// Gets or sets the create date of the record. + /// + /// + /// The create date. + /// + [Required] + public DateTime CreateDate { get; set; } + } +} \ No newline at end of file diff --git a/SQLite.CodeFirst/Entities/IHistory.cs b/SQLite.CodeFirst/Entities/IHistory.cs new file mode 100644 index 0000000..cbaf2e3 --- /dev/null +++ b/SQLite.CodeFirst/Entities/IHistory.cs @@ -0,0 +1,12 @@ +using System; + +namespace SQLite.CodeFirst +{ + public interface IHistory + { + int Id { get; set; } + string Hash { get; set; } + string Context { get; set; } + DateTime CreateDate { get; set; } + } +} \ No newline at end of file diff --git a/SQLite.CodeFirst/IDatabaseCreator.cs b/SQLite.CodeFirst/IDatabaseCreator.cs new file mode 100644 index 0000000..cf3797a --- /dev/null +++ b/SQLite.CodeFirst/IDatabaseCreator.cs @@ -0,0 +1,7 @@ +namespace SQLite.CodeFirst +{ + public interface IDatabaseCreator + { + void Create(); + } +} \ No newline at end of file diff --git a/SQLite.CodeFirst/ISqliteSqlGenerator.cs b/SQLite.CodeFirst/ISqliteSqlGenerator.cs new file mode 100644 index 0000000..06cf528 --- /dev/null +++ b/SQLite.CodeFirst/ISqliteSqlGenerator.cs @@ -0,0 +1,7 @@ +namespace SQLite.CodeFirst +{ + public interface ISqlGenerator + { + string Generate(); + } +} \ No newline at end of file diff --git a/SQLite.CodeFirst/Builder/ColumnStatementCollectionBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/ColumnStatementCollectionBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs diff --git a/SQLite.CodeFirst/Builder/CreateDatabaseStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/CreateDatabaseStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs diff --git a/SQLite.CodeFirst/Builder/CreateIndexStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateIndexStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/CreateIndexStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/CreateIndexStatementBuilder.cs diff --git a/SQLite.CodeFirst/Builder/CreateTableStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/CreateTableStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs diff --git a/SQLite.CodeFirst/Builder/ForeignKeyStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/ForeignKeyStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs diff --git a/SQLite.CodeFirst/Builder/IStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/IStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/IStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/IStatementBuilder.cs diff --git a/SQLite.CodeFirst/Builder/NameCreators/IndexNameCreator.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/IndexNameCreator.cs similarity index 100% rename from SQLite.CodeFirst/Builder/NameCreators/IndexNameCreator.cs rename to SQLite.CodeFirst/Internal/Builder/NameCreators/IndexNameCreator.cs diff --git a/SQLite.CodeFirst/Builder/NameCreators/SpecialChars.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/SpecialChars.cs similarity index 100% rename from SQLite.CodeFirst/Builder/NameCreators/SpecialChars.cs rename to SQLite.CodeFirst/Internal/Builder/NameCreators/SpecialChars.cs diff --git a/SQLite.CodeFirst/Builder/NameCreators/TableNameCreator.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/TableNameCreator.cs similarity index 100% rename from SQLite.CodeFirst/Builder/NameCreators/TableNameCreator.cs rename to SQLite.CodeFirst/Internal/Builder/NameCreators/TableNameCreator.cs diff --git a/SQLite.CodeFirst/Builder/PrimaryKeyStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/PrimaryKeyStatementBuilder.cs similarity index 100% rename from SQLite.CodeFirst/Builder/PrimaryKeyStatementBuilder.cs rename to SQLite.CodeFirst/Internal/Builder/PrimaryKeyStatementBuilder.cs diff --git a/SQLite.CodeFirst/Convention/SqliteForeignKeyIndexConvention.cs b/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs similarity index 100% rename from SQLite.CodeFirst/Convention/SqliteForeignKeyIndexConvention.cs rename to SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs diff --git a/SQLite.CodeFirst/Extensions/EntityTypeExtension.cs b/SQLite.CodeFirst/Internal/Extensions/EntityTypeExtension.cs similarity index 100% rename from SQLite.CodeFirst/Extensions/EntityTypeExtension.cs rename to SQLite.CodeFirst/Internal/Extensions/EntityTypeExtension.cs diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/ColumnConstraintCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/ColumnConstraintCollection.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnConstraint/ColumnConstraintCollection.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/ColumnConstraintCollection.cs diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraint.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraint.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraint.cs diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraintCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraintCollection.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraintCollection.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraintCollection.cs diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/MaxLengthConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/MaxLengthConstraint.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnConstraint/MaxLengthConstraint.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/MaxLengthConstraint.cs diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/NotNullConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/NotNullConstraint.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnConstraint/NotNullConstraint.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/NotNullConstraint.cs diff --git a/SQLite.CodeFirst/Statement/ColumnStatement.cs b/SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnStatement.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs diff --git a/SQLite.CodeFirst/Statement/ColumnStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnStatementCollection.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ColumnStatementCollection.cs rename to SQLite.CodeFirst/Internal/Statement/ColumnStatementCollection.cs diff --git a/SQLite.CodeFirst/Statement/CreateDatabaseStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateDatabaseStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/CreateDatabaseStatement.cs rename to SQLite.CodeFirst/Internal/Statement/CreateDatabaseStatement.cs diff --git a/SQLite.CodeFirst/Statement/CreateIndexStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateIndexStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/CreateIndexStatement.cs rename to SQLite.CodeFirst/Internal/Statement/CreateIndexStatement.cs diff --git a/SQLite.CodeFirst/Statement/CreateIndexStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/CreateIndexStatementCollection.cs similarity index 100% rename from SQLite.CodeFirst/Statement/CreateIndexStatementCollection.cs rename to SQLite.CodeFirst/Internal/Statement/CreateIndexStatementCollection.cs diff --git a/SQLite.CodeFirst/Statement/CreateTableStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateTableStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/CreateTableStatement.cs rename to SQLite.CodeFirst/Internal/Statement/CreateTableStatement.cs diff --git a/SQLite.CodeFirst/Statement/ForeignKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/ForeignKeyStatement.cs rename to SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs diff --git a/SQLite.CodeFirst/Statement/IStatement.cs b/SQLite.CodeFirst/Internal/Statement/IStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/IStatement.cs rename to SQLite.CodeFirst/Internal/Statement/IStatement.cs diff --git a/SQLite.CodeFirst/Statement/IStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/IStatementCollection.cs similarity index 100% rename from SQLite.CodeFirst/Statement/IStatementCollection.cs rename to SQLite.CodeFirst/Internal/Statement/IStatementCollection.cs diff --git a/SQLite.CodeFirst/Statement/PrimaryKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/PrimaryKeyStatement.cs similarity index 100% rename from SQLite.CodeFirst/Statement/PrimaryKeyStatement.cs rename to SQLite.CodeFirst/Internal/Statement/PrimaryKeyStatement.cs diff --git a/SQLite.CodeFirst/Utility/AssociationTypeWrapper.cs b/SQLite.CodeFirst/Internal/Utility/AssociationTypeWrapper.cs similarity index 100% rename from SQLite.CodeFirst/Utility/AssociationTypeWrapper.cs rename to SQLite.CodeFirst/Internal/Utility/AssociationTypeWrapper.cs diff --git a/SQLite.CodeFirst/SqliteConnectionStringParser.cs b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs similarity index 91% rename from SQLite.CodeFirst/SqliteConnectionStringParser.cs rename to SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs index 8d7d12d..b87f8b3 100644 --- a/SQLite.CodeFirst/SqliteConnectionStringParser.cs +++ b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Globalization; -namespace SQLite.CodeFirst +namespace SQLite.CodeFirst.Utility { - internal static class SqliteConnectionStringParser + internal static class ConnectionStringParser { private const string DataDirectoryToken = "|datadirectory|"; private const char KeyValuePairSeperator = ';'; @@ -12,7 +12,7 @@ internal static class SqliteConnectionStringParser private const int KeyPosition = 0; private const int ValuePosition = 1; - public static IDictionary ParseSqliteConnectionString(string connectionString) + public static IDictionary ParseConnectionString(string connectionString) { connectionString = connectionString.Trim(); string[] keyValuePairs = connectionString.Split(KeyValuePairSeperator); @@ -32,7 +32,7 @@ public static IDictionary ParseSqliteConnectionString(string con public static string GetDataSource(string connectionString) { - var path = ExpandDataDirectory(ParseSqliteConnectionString(connectionString)["data source"]); + var path = ExpandDataDirectory(ParseConnectionString(connectionString)["data source"]); // remove quotation mark if exists path = path.Trim('"'); return path; diff --git a/SQLite.CodeFirst/Internal/Utility/HashCreator.cs b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs new file mode 100644 index 0000000..3b4f366 --- /dev/null +++ b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs @@ -0,0 +1,18 @@ +using System; +using System.Security.Cryptography; +using System.Text; + +namespace SQLite.CodeFirst.Utility +{ + internal static class HashCreator + { + public static string CreateHash(string data) + { + byte[] dataBytes = Encoding.ASCII.GetBytes(data); + SHA512 sha512 = new SHA512Managed(); + byte[] hashBytes = sha512.ComputeHash(dataBytes); + string hash = Convert.ToBase64String(hashBytes); + return hash; + } + } +} diff --git a/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs b/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs new file mode 100644 index 0000000..459e2ee --- /dev/null +++ b/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs @@ -0,0 +1,19 @@ +using System; + +namespace SQLite.CodeFirst.Utility +{ + internal class HistoryEntityTypeValidator + { + public static void EnsureValidType(Type historyEntityType) + { + if (!typeof(IHistory).IsAssignableFrom(historyEntityType)) + { + throw new InvalidOperationException("The Type " + historyEntityType.Name + " does not implement the IHistory interface."); + } + if (historyEntityType.GetConstructor(Type.EmptyTypes) == null) + { + throw new InvalidOperationException("The Type " + historyEntityType.Name + " does not provide an parameterless constructor."); + } + } + } +} diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj b/SQLite.CodeFirst/SQLite.CodeFirst.csproj index 0ce1922..a7bc256 100644 --- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj +++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj @@ -75,6 +75,7 @@ + @@ -85,40 +86,48 @@ Properties\AssemblyVersionInfo.cs - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - + + + + + + + + + + + diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj.DotSettings b/SQLite.CodeFirst/SQLite.CodeFirst.csproj.DotSettings new file mode 100644 index 0000000..588c632 --- /dev/null +++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj.DotSettings @@ -0,0 +1,8 @@ + + True + True + True + True + True + True + True \ No newline at end of file diff --git a/SQLite.CodeFirst/SqliteDatabaseCreator.cs b/SQLite.CodeFirst/SqliteDatabaseCreator.cs index 11e5d62..ea1aa15 100644 --- a/SQLite.CodeFirst/SqliteDatabaseCreator.cs +++ b/SQLite.CodeFirst/SqliteDatabaseCreator.cs @@ -1,7 +1,5 @@ using System.Data.Entity; using System.Data.Entity.Infrastructure; -using SQLite.CodeFirst.Builder; -using SQLite.CodeFirst.Statement; namespace SQLite.CodeFirst { @@ -9,17 +7,17 @@ namespace SQLite.CodeFirst /// Creates a SQLite-Database based on a Entity Framework and . /// This creator can be used standalone or within an initializer. /// - public class SqliteDatabaseCreator + public class SqliteDatabaseCreator : IDatabaseCreator { private readonly Database db; private readonly DbModel model; - /// - /// Initializes a new instance of the class. - /// - /// The database. - /// The model. - public SqliteDatabaseCreator(Database db, DbModel model) + /// + /// Initializes a new instance of the class. + /// + /// The database. + /// The model. + public SqliteDatabaseCreator(Database db, DbModel model) { this.db = db; this.model = model; @@ -30,10 +28,9 @@ public SqliteDatabaseCreator(Database db, DbModel model) /// public void Create() { - IStatementBuilder statementBuilder = new CreateDatabaseStatementBuilder(model.StoreModel); - IStatement statement = statementBuilder.BuildStatement(); - string sql = statement.CreateStatement(); - db.ExecuteSqlCommand(sql); + var sqliteSqlGenerator = new SqliteSqlGenerator(model.StoreModel); + string sql = sqliteSqlGenerator.Generate(); + db.ExecuteSqlCommand(TransactionalBehavior.EnsureTransaction, sql); } } } diff --git a/SQLite.CodeFirst/SqliteSqlGenerator.cs b/SQLite.CodeFirst/SqliteSqlGenerator.cs new file mode 100644 index 0000000..7f2a210 --- /dev/null +++ b/SQLite.CodeFirst/SqliteSqlGenerator.cs @@ -0,0 +1,29 @@ +using System.Data.Entity.Core.Metadata.Edm; +using SQLite.CodeFirst.Builder; +using SQLite.CodeFirst.Statement; + +namespace SQLite.CodeFirst +{ + /// + /// Generates the SQL statement to create a database, based on a . + /// + public class SqliteSqlGenerator : ISqlGenerator + { + private readonly EdmModel storeModel; + + public SqliteSqlGenerator(EdmModel storeModel) + { + this.storeModel = storeModel; + } + + /// + /// Generates the SQL statement. + /// + public string Generate() + { + IStatementBuilder statementBuilder = new CreateDatabaseStatementBuilder(storeModel); + IStatement statement = statementBuilder.BuildStatement(); + return statement.CreateStatement(); + } + } +} From b33fd00a96540025772686222a41f9429d32904f Mon Sep 17 00:00:00 2001 From: Marc Sallin Date: Sun, 17 Jan 2016 14:18:50 +0100 Subject: [PATCH 02/15] #16:Added additional framework dependency. --- SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec | 1 + 1 file changed, 1 insertion(+) diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec b/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec index f34f826..6236557 100644 --- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec +++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec @@ -18,6 +18,7 @@ + From ecf89c3c47c85851798a3bce5f0d6a52a4ac3c14 Mon Sep 17 00:00:00 2001 From: Marc Sallin Date: Sun, 17 Jan 2016 14:21:12 +0100 Subject: [PATCH 03/15] #16: Removed unnecessary dependencies to SQLite. --- SQLite.CodeFirst/SQLite.CodeFirst.csproj | 2 -- SQLite.CodeFirst/packages.config | 4 ---- 2 files changed, 6 deletions(-) diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj b/SQLite.CodeFirst/SQLite.CodeFirst.csproj index a7bc256..92526ab 100644 --- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj +++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj @@ -140,8 +140,6 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - \ No newline at end of file diff --git a/SQLite.CodeFirst/packages.config b/SQLite.CodeFirst/packages.config index 57e2eaa..cb3371a 100644 --- a/SQLite.CodeFirst/packages.config +++ b/SQLite.CodeFirst/packages.config @@ -1,8 +1,4 @@  - - - - \ No newline at end of file From 43cdab0920d0f6bb6b2658749385ed66c5ac448b Mon Sep 17 00:00:00 2001 From: Marc Sallin Date: Sun, 17 Jan 2016 14:21:30 +0100 Subject: [PATCH 04/15] #16: Updated to the newest SQLite version. --- SQLite.CodeFirst.Console/App.config | 6 ++++-- .../SQLite.CodeFirst.Console.csproj | 18 ++++++++++-------- SQLite.CodeFirst.Console/packages.config | 8 ++++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/SQLite.CodeFirst.Console/App.config b/SQLite.CodeFirst.Console/App.config index b7d5048..9fa9559 100644 --- a/SQLite.CodeFirst.Console/App.config +++ b/SQLite.CodeFirst.Console/App.config @@ -11,9 +11,10 @@ - - + + + @@ -25,6 +26,7 @@ + \ No newline at end of file diff --git a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj index 4c517f7..c1379f4 100644 --- a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj +++ b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj @@ -53,15 +53,17 @@ - - ..\packages\System.Data.SQLite.Core.1.0.97.0\lib\net451\System.Data.SQLite.dll + + ..\packages\System.Data.SQLite.Core.1.0.99.0\lib\net451\System.Data.SQLite.dll True - - ..\packages\System.Data.SQLite.EF6.1.0.97.0\lib\net451\System.Data.SQLite.EF6.dll + + ..\packages\System.Data.SQLite.EF6.1.0.99.0\lib\net451\System.Data.SQLite.EF6.dll + True - - ..\packages\System.Data.SQLite.Linq.1.0.97.0\lib\net451\System.Data.SQLite.Linq.dll + + ..\packages\System.Data.SQLite.Linq.1.0.99.0\lib\net451\System.Data.SQLite.Linq.dll + True @@ -104,10 +106,10 @@ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + - +