├── .gitignore ├── LICENSE.txt ├── System.Data.SQLite.EF6.Migrations.Test ├── App.config ├── DatabaseFixture.cs ├── MigrationTest.cs ├── Models │ ├── Context.cs │ ├── ContextMigrationConfiguration.cs │ └── Model.cs ├── Properties │ └── AssemblyInfo.cs ├── System.Data.SQLite.EF6.Migrations.Test.csproj └── packages.config ├── System.Data.SQLite.EF6.Migrations.sln ├── System.Data.SQLite.EF6.Migrations.sln.DotSettings ├── System.Data.SQLite.EF6.Migrations ├── App.config ├── LiteralHelpers.cs ├── MetadataHelpers.cs ├── Properties │ └── AssemblyInfo.cs ├── SQLiteDdlBuilder.cs ├── SQLiteDmlBuilder.cs ├── SQLiteMigrationSqlGenerator.cs ├── SQLiteProviderManifestHelper.cs ├── SQLiteProviderServicesHelper.cs ├── System.Data.SQLite.EF6.Migrations.csproj ├── TypeUsageHelpers.cs └── packages.config └── docs ├── README.md └── license.md /.gitignore: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # This .gitignore file was automatically created by Microsoft(R) Visual Studio. 3 | ################################################################################ 4 | 5 | /.nuget 6 | /NuGet.Packager 7 | /packages 8 | /System.Data.SQLite.EF6.Migrations/bin 9 | /System.Data.SQLite.EF6.Migrations/obj 10 | /System.Data.SQLite.EF6.Migrations.Test/bin 11 | /System.Data.SQLite.EF6.Migrations.Test/obj 12 | *.user 13 | *.suo 14 | .vs 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Microsoft Public License (MS-PL) 2 | 3 | This license governs use of the accompanying software. If you use the software, you 4 | accept this license. If you do not accept the license, do not use the software. 5 | 6 | 1. Definitions 7 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the 8 | same meaning here as under U.S. copyright law. 9 | A "contribution" is the original software, or any additions or changes to the software. 10 | A "contributor" is any person that distributes its contribution under this license. 11 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 12 | 13 | 2. Grant of Rights 14 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 15 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 16 | 17 | 3. Conditions and Limitations 18 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 19 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 20 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 21 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 22 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 23 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/DatabaseFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.IO; 3 | 4 | namespace System.Data.SQLite.Migrations.Test 5 | { 6 | public class DatabaseFixture : IDisposable 7 | { 8 | private const string fileName = "Model01"; 9 | private readonly string databaseFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}.db3"); 10 | public DbConnection Connection { get; set; } 11 | 12 | public DatabaseFixture() 13 | { 14 | var connectionString = new SQLiteConnection($"Data Source={databaseFilePath};Version=3;"); 15 | Connection = new SQLiteConnection(connectionString); 16 | } 17 | 18 | public void Dispose() 19 | { 20 | Connection.Close(); 21 | Connection.Dispose(); 22 | 23 | //https://stackoverflow.com/a/8513453/1872200 24 | GC.Collect(); 25 | GC.WaitForPendingFinalizers(); 26 | 27 | File.Delete(databaseFilePath); 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/MigrationTest.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.Data.SQLite.EF6.Migrations.Test.Models; 3 | using System.Linq; 4 | using Xunit; 5 | 6 | namespace System.Data.SQLite.Migrations.Test 7 | { 8 | public class MigrationTest : IClassFixture 9 | { 10 | //Shared Context between Tests https://xunit.github.io/docs/shared-context.html 11 | private readonly DatabaseFixture databaseFixture; 12 | 13 | public MigrationTest(DatabaseFixture databaseFixture) => this.databaseFixture = databaseFixture; 14 | 15 | [Fact] 16 | public void UseAutoMigration_AutomaticMigrationsEnabled_TablesCreated() 17 | { 18 | var expectedTableNames = new[] { "Dependants", "sqlite_sequence", "Entities", "__MigrationHistory" }; 19 | var tableNames = GetAllTableNames(databaseFixture.Connection); 20 | Assert.Equal(expectedTableNames, tableNames); 21 | } 22 | 23 | [Fact] 24 | public void UseAutoMigration_WithInMemoryDatabase_TablesNotCreated() 25 | { 26 | var inMemoryDatabaseConnection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True"); 27 | var tableNames = GetAllTableNames(inMemoryDatabaseConnection); 28 | Assert.Empty(tableNames); 29 | } 30 | 31 | [Fact] 32 | public void AddEntity_AutomaticMigrationsEnabled_EntityAdded() 33 | { 34 | using (var context = new Context(databaseFixture.Connection)) 35 | { 36 | context.Dependants.Add( 37 | new Dependant() 38 | { 39 | Description = "Dependant description", 40 | MainEntity = new EF6.Migrations.Test.Models.Entity() 41 | { 42 | Description = "Entity description" 43 | } 44 | }); 45 | context.SaveChanges(); 46 | Assert.Equal(1, context.Entities.Count()); 47 | } 48 | 49 | using (var context = new Context(databaseFixture.Connection)) 50 | { 51 | Assert.Equal(1, context.Entities.Count()); 52 | } 53 | } 54 | 55 | private static string[] GetAllTableNames(DbConnection connection) 56 | { 57 | using (var context = new Context(connection)) 58 | { 59 | return context.Database.SqlQuery( 60 | "SELECT name as TableName FROM sqlite_master WHERE type = 'table'" 61 | ).ToArray(); 62 | } 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/Models/Context.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Common; 2 | using System.Data.Entity; 3 | 4 | namespace System.Data.SQLite.EF6.Migrations.Test.Models 5 | { 6 | class Context : DbContext 7 | { 8 | static Context() => 9 | Database.SetInitializer(new MigrateDatabaseToLatestVersion(true)); 10 | 11 | public Context(DbConnection connection) : base(connection, false) { } 12 | 13 | public DbSet Entities { get; set; } 14 | public DbSet Dependants { get; set; } 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/Models/ContextMigrationConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System.Data.Entity.Migrations; 2 | 3 | namespace System.Data.SQLite.EF6.Migrations.Test.Models 4 | { 5 | internal sealed class ContextMigrationConfiguration : DbMigrationsConfiguration 6 | { 7 | public ContextMigrationConfiguration() 8 | { 9 | AutomaticMigrationsEnabled = true; 10 | AutomaticMigrationDataLossAllowed = true; 11 | SetSqlGenerator("System.Data.SQLite", new SQLiteMigrationSqlGenerator()); 12 | } 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/Models/Model.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace System.Data.SQLite.EF6.Migrations.Test.Models 4 | { 5 | public class Entity 6 | { 7 | public int Id { get; set; } 8 | 9 | [MaxLength(50)] 10 | public string Description { get; set; } 11 | } 12 | 13 | public class Dependant 14 | { 15 | public int Id { get; set; } 16 | 17 | [MaxLength(50)] 18 | public string Description { get; set; } 19 | 20 | public virtual Entity MainEntity { get; set; } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SQLiteEF6Migration")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SQLiteEF6Migration")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b60dbf15-6b04-4355-94e7-59793d703b8f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.104.0")] 36 | [assembly: AssemblyFileVersion("1.0.104.0")] 37 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/System.Data.SQLite.EF6.Migrations.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | Debug 9 | AnyCPU 10 | {B5A7CD34-89E4-48FB-9716-3F8F62F01217} 11 | Library 12 | Properties 13 | System.Data.SQLite.EF6.Migrations.Test 14 | System.Data.SQLite.EF6.Migrations.Test 15 | v4.5.2 16 | 512 17 | 18 | 19 | ..\ 20 | true 21 | 22 | 23 | 24 | AnyCPU 25 | true 26 | full 27 | false 28 | bin\Debug\ 29 | DEBUG;TRACE 30 | prompt 31 | 4 32 | 33 | 34 | AnyCPU 35 | pdbonly 36 | true 37 | bin\Release\ 38 | TRACE 39 | prompt 40 | 4 41 | 42 | 43 | 44 | 45 | 46 | 47 | ..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.dll 48 | 49 | 50 | ..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.SqlServer.dll 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | ..\packages\System.Data.SQLite.Core.1.0.112.2\lib\net40\System.Data.SQLite.dll 59 | 60 | 61 | ..\packages\System.Data.SQLite.EF6.1.0.112.2\lib\net40\System.Data.SQLite.EF6.dll 62 | 63 | 64 | ..\packages\System.Data.SQLite.Linq.1.0.112.2\lib\net40\System.Data.SQLite.Linq.dll 65 | 66 | 67 | ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll 68 | 69 | 70 | ..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll 71 | 72 | 73 | ..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll 74 | 75 | 76 | ..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Designer 90 | 91 | 92 | 93 | 94 | 95 | {6b715f5a-8f7d-427c-8873-216674b4b401} 96 | System.Data.SQLite.Migrations 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 126 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.Test/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30516.212 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C242E496-71CD-4AF5-B5D2-AAD23965676D}" 7 | ProjectSection(SolutionItems) = preProject 8 | .gitignore = .gitignore 9 | LICENSE.txt = LICENSE.txt 10 | docs\README.md = docs\README.md 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{6B057F36-7C54-47C2-B219-2A4784DC0594}" 14 | ProjectSection(SolutionItems) = preProject 15 | .nuget\NuGet.Config = .nuget\NuGet.Config 16 | .nuget\NuGet.exe = .nuget\NuGet.exe 17 | .nuget\NuGet.targets = .nuget\NuGet.targets 18 | EndProjectSection 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SQLite.EF6.Migrations", "System.Data.SQLite.EF6.Migrations\System.Data.SQLite.EF6.Migrations.csproj", "{6B715F5A-8F7D-427C-8873-216674B4B401}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Data.SQLite.EF6.Migrations.Test", "System.Data.SQLite.EF6.Migrations.Test\System.Data.SQLite.EF6.Migrations.Test.csproj", "{B5A7CD34-89E4-48FB-9716-3F8F62F01217}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {6B715F5A-8F7D-427C-8873-216674B4B401}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {6B715F5A-8F7D-427C-8873-216674B4B401}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {6B715F5A-8F7D-427C-8873-216674B4B401}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {6B715F5A-8F7D-427C-8873-216674B4B401}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {B5A7CD34-89E4-48FB-9716-3F8F62F01217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {B5A7CD34-89E4-48FB-9716-3F8F62F01217}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {B5A7CD34-89E4-48FB-9716-3F8F62F01217}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {B5A7CD34-89E4-48FB-9716-3F8F62F01217}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | GlobalSection(ExtensibilityGlobals) = postSolution 43 | SolutionGuid = {907EB4A5-9C45-4ABB-A9D2-284B83F95361} 44 | EndGlobalSection 45 | EndGlobal 46 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | SQ -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/LiteralHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace System.Data.SQLite.EF6.Migrations 5 | { 6 | static class LiteralHelpers 7 | { 8 | static private readonly char[] HexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 9 | 10 | public static string ToSqlString(byte[] value) 11 | { 12 | return " X'" + ByteArrayToBinaryString(value) + "' "; 13 | } 14 | 15 | public static string ToSqlString(bool value) 16 | { 17 | return value ? "true" : "false"; 18 | } 19 | 20 | static string ByteArrayToBinaryString(Byte[] binaryArray) 21 | { 22 | StringBuilder sb = new StringBuilder(binaryArray.Length * 2); 23 | for (int i = 0; i < binaryArray.Length; i++) 24 | { 25 | sb.Append(HexDigits[(binaryArray[i] & 0xF0) >> 4]).Append(HexDigits[binaryArray[i] & 0x0F]); 26 | } 27 | return sb.ToString(); 28 | } 29 | 30 | public static string ToSqlString(string value) 31 | { 32 | // In SQLite everything's unicode 33 | return "'" + value.Replace("'", "''") + "'"; 34 | } 35 | 36 | public static string ToSqlString(Guid value) 37 | { 38 | // In SQLite everything's unicode 39 | return "'P" + value.ToString() + "'"; 40 | } 41 | 42 | /// 43 | /// Transform the given value in a string formatted in a valid format, 44 | /// that represents a date and a time 45 | /// 46 | /// The value to format 47 | /// The string that represents the today time, to include in a where clause 48 | public static string SqlDateTime(System.DateTime time) 49 | { 50 | return string.Format("#{0:MM/dd/yyyy} {0:HH.mm.ss}#", time); 51 | } 52 | 53 | /// 54 | /// Transform the given value in a string formatted in a valid Jet format, 55 | /// that represents the today time 56 | /// 57 | /// The value to format 58 | /// The string that represents the today time, to include in a where clause 59 | public static string SqlDayTime(System.TimeSpan time) 60 | { 61 | return string.Format("#{0:00}.{1:00}.{2:00}#", time.Hours, time.Minutes, time.Seconds); 62 | } 63 | 64 | /// 65 | /// Transform the given value in a string formatted in a valid Jet format, 66 | /// that represents the today time 67 | /// 68 | /// The value to format 69 | /// The string that represents the today time, to include in a where clause 70 | public static string SqlDayTime(System.DateTime time) 71 | { 72 | return string.Format("#{0:00}.{1:00}.{2:00}#", time.Hour, time.Minute, time.Second); 73 | } 74 | 75 | 76 | 77 | 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/MetadataHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity.Core.Metadata.Edm; 4 | 5 | namespace System.Data.SQLite.EF6.Migrations 6 | { 7 | /// 8 | /// A set of static helpers for type metadata 9 | /// 10 | static class MetadataHelpers 11 | { 12 | #region Type Helpers 13 | 14 | /// 15 | /// Cast the EdmType of the given type usage to the given TEdmType 16 | /// 17 | /// 18 | /// 19 | /// 20 | internal static TEdmType GetEdmType(TypeUsage typeUsage) 21 | where TEdmType : EdmType 22 | { 23 | return (TEdmType)typeUsage.EdmType; 24 | } 25 | 26 | /// 27 | /// Gets the TypeUsage of the elment if the given type is a collection type 28 | /// 29 | /// 30 | /// 31 | internal static TypeUsage GetElementTypeUsage(TypeUsage type) 32 | { 33 | if (MetadataHelpers.IsCollectionType(type)) 34 | { 35 | return ((CollectionType)type.EdmType).TypeUsage; 36 | } 37 | return null; 38 | } 39 | 40 | /// 41 | /// Retrieves the properties of in the EdmType underlying the input type usage, 42 | /// if that EdmType is a structured type (EntityType, RowType). 43 | /// 44 | /// 45 | /// 46 | internal static IList GetProperties(TypeUsage typeUsage) 47 | { 48 | return MetadataHelpers.GetProperties(typeUsage.EdmType); 49 | } 50 | 51 | /// 52 | /// Retrieves the properties of the given EdmType, if it is 53 | /// a structured type (EntityType, RowType). 54 | /// 55 | /// 56 | /// 57 | internal static IList GetProperties(EdmType edmType) 58 | { 59 | switch (edmType.BuiltInTypeKind) 60 | { 61 | case BuiltInTypeKind.ComplexType: 62 | return ((ComplexType)edmType).Properties; 63 | case BuiltInTypeKind.EntityType: 64 | return ((EntityType)edmType).Properties; 65 | case BuiltInTypeKind.RowType: 66 | return ((RowType)edmType).Properties; 67 | default: 68 | return new List(); 69 | } 70 | } 71 | 72 | /// 73 | /// Is the given type usage over a collection type 74 | /// 75 | /// 76 | /// 77 | internal static bool IsCollectionType(TypeUsage typeUsage) 78 | { 79 | return MetadataHelpers.IsCollectionType(typeUsage.EdmType); 80 | } 81 | 82 | /// 83 | /// Is the given type a collection type 84 | /// 85 | /// 86 | /// 87 | internal static bool IsCollectionType(EdmType type) 88 | { 89 | return (BuiltInTypeKind.CollectionType == type.BuiltInTypeKind); 90 | } 91 | 92 | /// 93 | /// Is the given type usage over a primitive type 94 | /// 95 | /// 96 | /// 97 | internal static bool IsPrimitiveType(TypeUsage type) 98 | { 99 | return MetadataHelpers.IsPrimitiveType(type.EdmType); 100 | } 101 | 102 | /// 103 | /// Is the given type a primitive type 104 | /// 105 | /// 106 | /// 107 | internal static bool IsPrimitiveType(EdmType type) 108 | { 109 | return (BuiltInTypeKind.PrimitiveType == type.BuiltInTypeKind); 110 | } 111 | 112 | /// 113 | /// Is the given type usage over a row type 114 | /// 115 | /// 116 | /// 117 | internal static bool IsRowType(TypeUsage type) 118 | { 119 | return MetadataHelpers.IsRowType(type.EdmType); 120 | } 121 | 122 | /// 123 | /// Is the given type usage over an entity type 124 | /// 125 | /// 126 | /// 127 | internal static bool IsEntityType(TypeUsage type) 128 | { 129 | return MetadataHelpers.IsEntityType(type.EdmType); 130 | } 131 | 132 | /// 133 | /// Is the given type a row type 134 | /// 135 | /// 136 | /// 137 | internal static bool IsRowType(EdmType type) 138 | { 139 | return (BuiltInTypeKind.RowType == type.BuiltInTypeKind); 140 | } 141 | 142 | /// 143 | /// Is the given type an Enity Type 144 | /// 145 | /// 146 | /// 147 | internal static bool IsEntityType(EdmType type) 148 | { 149 | return (BuiltInTypeKind.EntityType == type.BuiltInTypeKind); 150 | } 151 | 152 | 153 | /// 154 | /// Gets the value for the metadata property with the given name 155 | /// 156 | /// 157 | /// 158 | /// 159 | /// 160 | internal static T TryGetValueForMetadataProperty(MetadataItem item, string propertyName) 161 | { 162 | MetadataProperty property; 163 | if (!item.MetadataProperties.TryGetValue(propertyName, true, out property)) 164 | { 165 | return default(T); 166 | } 167 | 168 | return (T)property.Value; 169 | } 170 | 171 | internal static DbType GetDbType(PrimitiveTypeKind primitiveType) 172 | { 173 | switch (primitiveType) 174 | { 175 | case PrimitiveTypeKind.Binary: return DbType.Binary; 176 | case PrimitiveTypeKind.Boolean: return DbType.Boolean; 177 | case PrimitiveTypeKind.Byte: return DbType.Byte; 178 | case PrimitiveTypeKind.DateTime: return DbType.DateTime; 179 | case PrimitiveTypeKind.Decimal: return DbType.Decimal; 180 | case PrimitiveTypeKind.Double: return DbType.Double; 181 | case PrimitiveTypeKind.Single: return DbType.Single; 182 | case PrimitiveTypeKind.Guid: return DbType.Guid; 183 | case PrimitiveTypeKind.Int16: return DbType.Int16; 184 | case PrimitiveTypeKind.Int32: return DbType.Int32; 185 | case PrimitiveTypeKind.Int64: return DbType.Int64; 186 | //case PrimitiveTypeKind.Money: return DbType.Decimal; 187 | case PrimitiveTypeKind.SByte: return DbType.SByte; 188 | case PrimitiveTypeKind.String: return DbType.String; 189 | //case PrimitiveTypeKind.UInt16: return DbType.UInt16; 190 | //case PrimitiveTypeKind.UInt32: return DbType.UInt32; 191 | //case PrimitiveTypeKind.UInt64: return DbType.UInt64; 192 | //case PrimitiveTypeKind.Xml: return DbType.Xml; 193 | default: 194 | throw new InvalidOperationException(string.Format("Unknown PrimitiveTypeKind {0}", primitiveType)); 195 | } 196 | } 197 | 198 | #endregion 199 | 200 | 201 | internal static bool IsCanonicalFunction(EdmFunction function) 202 | { 203 | return (function.NamespaceName == "Edm"); 204 | } 205 | 206 | internal static bool IsStoreFunction(EdmFunction function) 207 | { 208 | return !IsCanonicalFunction(function); 209 | } 210 | 211 | // Returns ParameterDirection corresponding to given ParameterMode 212 | internal static ParameterDirection ParameterModeToParameterDirection(ParameterMode mode) 213 | { 214 | switch (mode) 215 | { 216 | case ParameterMode.In: 217 | return ParameterDirection.Input; 218 | 219 | case ParameterMode.InOut: 220 | return ParameterDirection.InputOutput; 221 | 222 | case ParameterMode.Out: 223 | return ParameterDirection.Output; 224 | 225 | case ParameterMode.ReturnValue: 226 | return ParameterDirection.ReturnValue; 227 | 228 | default: 229 | throw new ArgumentException("Unrecognized parameter mode", "mode"); 230 | } 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("System.Data.SQLite.Migrations")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("System.Data.SQLite.Migrations")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("70d93971-51ba-4994-a836-f3205e970c7f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.107.0")] 36 | [assembly: AssemblyFileVersion("1.0.107.0")] 37 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/SQLiteDdlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity.Core.Metadata.Edm; 4 | using System.Text; 5 | 6 | namespace System.Data.SQLite.EF6.Migrations 7 | { 8 | class SQLiteDdlBuilder 9 | { 10 | private readonly StringBuilder _stringBuilder = new StringBuilder(); 11 | 12 | public string GetCommandText() 13 | { 14 | return _stringBuilder.ToString(); 15 | } 16 | 17 | 18 | public void AppendStringLiteral(string literalValue) 19 | { 20 | AppendSql("'" + literalValue.Replace("'", "''") + "'"); 21 | } 22 | 23 | 24 | public void AppendIdentifier(string identifier) 25 | { 26 | string correctIdentifier; 27 | 28 | correctIdentifier = SQLiteProviderManifestHelper.RemoveDbo(identifier); 29 | 30 | if (correctIdentifier.Length > SQLiteProviderManifestHelper.MaxObjectNameLength) 31 | { 32 | string guid = Guid.NewGuid().ToString().Replace("-", ""); 33 | correctIdentifier = correctIdentifier.Substring(0, SQLiteProviderManifestHelper.MaxObjectNameLength - guid.Length) + guid; 34 | } 35 | 36 | 37 | AppendSql(SQLiteProviderManifestHelper.QuoteIdentifier(correctIdentifier)); 38 | } 39 | 40 | 41 | public void AppendIdentifierList(IEnumerable identifiers) 42 | { 43 | bool first = true; 44 | foreach (var identifier in identifiers) 45 | { 46 | if (first) 47 | first = false; 48 | else 49 | AppendSql(", "); 50 | AppendIdentifier(identifier); 51 | } 52 | } 53 | 54 | public void AppendType(EdmProperty column) 55 | { 56 | AppendType(column.TypeUsage, column.Nullable, column.TypeUsage.GetIsIdentity()); 57 | } 58 | 59 | public void AppendType(TypeUsage typeUsage, bool isNullable, bool isIdentity) 60 | { 61 | 62 | bool isTimestamp = false; 63 | 64 | string sqliteTypeName = typeUsage.EdmType.Name; 65 | string sqliteLength = ""; 66 | 67 | 68 | switch (sqliteTypeName) 69 | { 70 | case "decimal": 71 | case "numeric": 72 | sqliteLength = string.Format(System.Globalization.CultureInfo.InvariantCulture, "({0}, {1})", typeUsage.GetPrecision(), typeUsage.GetScale()); 73 | break; 74 | case "binary": 75 | case "varbinary": 76 | case "varchar": 77 | case "nvarchar": 78 | case "char": 79 | case "nchar": 80 | sqliteLength = string.Format("({0})", typeUsage.GetMaxLength()); 81 | break; 82 | default: 83 | break; 84 | } 85 | 86 | AppendSql(sqliteTypeName); 87 | AppendSql(sqliteLength); 88 | AppendSql(isNullable ? " null" : " not null"); 89 | 90 | if (isTimestamp) 91 | ;// nothing to generate for identity 92 | else if (isIdentity && sqliteTypeName == "guid") 93 | AppendSql(" default GenGUID()"); 94 | else if (isIdentity) 95 | AppendSql(" constraint primary key autoincrement"); 96 | } 97 | 98 | /// 99 | /// Appends raw SQL into the string builder. 100 | /// 101 | /// Raw SQL string to append into the string builder. 102 | public void AppendSql(string text) 103 | { 104 | _stringBuilder.Append(text); 105 | } 106 | 107 | /// 108 | /// Appends raw SQL into the string builder. 109 | /// 110 | /// The format. 111 | /// The p. 112 | public void AppendSql(string format, params object[] p) 113 | { 114 | _stringBuilder.AppendFormat(format, p); 115 | } 116 | 117 | 118 | /// 119 | /// Appends new line for visual formatting or for ending a comment. 120 | /// 121 | public void AppendNewLine() 122 | { 123 | _stringBuilder.Append("\r\n"); 124 | } 125 | 126 | 127 | public string CreateConstraintName(string constraint, string objectName) 128 | { 129 | objectName = SQLiteProviderManifestHelper.RemoveDbo(objectName); 130 | 131 | string name = string.Format("{0}_{1}", constraint, objectName); 132 | 133 | 134 | if (name.Length + 9 > SQLiteProviderManifestHelper.MaxObjectNameLength) 135 | name = name.Substring(0, SQLiteProviderManifestHelper.MaxObjectNameLength - 9); 136 | 137 | name += "_" + GetRandomString(); 138 | 139 | return name; 140 | } 141 | 142 | // Returns an eigth nibbles string 143 | protected string GetRandomString() 144 | { 145 | Random random = new Random(); 146 | string randomValue = ""; 147 | for (int n = 0; n < 8; n++) 148 | { 149 | byte b = (byte)random.Next(15); 150 | randomValue += string.Format("{0:x1}", b); 151 | } 152 | 153 | return randomValue; 154 | } 155 | 156 | 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/SQLiteDmlBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using System.Data.Entity.Core.Common.CommandTrees; 5 | using System.Data.Entity.Core.Metadata.Edm; 6 | using System.Diagnostics; 7 | using System.Globalization; 8 | using System.Linq; 9 | using System.Text; 10 | 11 | namespace System.Data.SQLite.EF6.Migrations 12 | { 13 | /// 14 | /// Class generating SQL for a DML command tree. 15 | /// 16 | internal static class SQLiteDmlBuilder 17 | { 18 | private static Guid _lastGuid; 19 | 20 | const int COMMANDTEXT_STRINGBUILDER_INITIALCAPACITY = 256; 21 | 22 | internal static string GenerateUpdateSql(DbUpdateCommandTree tree, out List parameters, bool insertParametersValuesInSql = false) 23 | { 24 | StringBuilder commandText = new StringBuilder(COMMANDTEXT_STRINGBUILDER_INITIALCAPACITY); 25 | ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, tree.Returning != null, insertParametersValuesInSql); 26 | 27 | commandText.Append("update "); 28 | tree.Target.Expression.Accept(translator); 29 | commandText.AppendLine(); 30 | 31 | // set c1 = ..., c2 = ..., ... 32 | bool first = true; 33 | commandText.Append("set "); 34 | foreach (DbSetClause setClause in tree.SetClauses) 35 | { 36 | if (first) { first = false; } 37 | else { commandText.Append(", "); } 38 | setClause.Property.Accept(translator); 39 | commandText.Append(" = "); 40 | setClause.Value.Accept(translator); 41 | } 42 | 43 | if (first) 44 | { 45 | // If first is still true, it indicates there were no set 46 | // clauses. Introduce a fake set clause so that: 47 | // - we acquire the appropriate locks 48 | // - server-gen columns (e.g. timestamp) get recomputed 49 | // 50 | // We use the following pattern: 51 | // 52 | // update Foo 53 | // set @i = 0 54 | // where ... 55 | DbParameter parameter = 56 | translator.CreateParameter( 57 | default(Int32), 58 | TypeUsage.CreateDefaultTypeUsage(PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.Int32))); 59 | 60 | commandText.Append(parameter.ParameterName); 61 | commandText.Append(" = 0"); 62 | } 63 | commandText.AppendLine(); 64 | 65 | // where c1 = ... AND c2 = ... 66 | commandText.Append("where "); 67 | tree.Predicate.Accept(translator); 68 | commandText.AppendLine(); 69 | 70 | // generate returning sql 71 | GenerateReturningSql(commandText, tree, translator, tree.Returning); 72 | 73 | parameters = translator.Parameters; 74 | return commandText.ToString(); 75 | } 76 | 77 | internal static string GenerateDeleteSql(DbDeleteCommandTree tree, out List parameters, bool insertParametersValuesInSql = false) 78 | { 79 | StringBuilder commandText = new StringBuilder(COMMANDTEXT_STRINGBUILDER_INITIALCAPACITY); 80 | ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, false, insertParametersValuesInSql); 81 | 82 | commandText.Append("delete from "); 83 | tree.Target.Expression.Accept(translator); 84 | commandText.AppendLine(); 85 | 86 | // where c1 = ... AND c2 = ... 87 | commandText.Append("where "); 88 | tree.Predicate.Accept(translator); 89 | 90 | parameters = translator.Parameters; 91 | return commandText.ToString(); 92 | } 93 | 94 | internal static string GenerateInsertSql(DbInsertCommandTree tree, out List parameters, bool insertParametersValuesInSql = false) 95 | { 96 | StringBuilder commandText = new StringBuilder(COMMANDTEXT_STRINGBUILDER_INITIALCAPACITY); 97 | ExpressionTranslator translator = new ExpressionTranslator(commandText, tree, tree.Returning != null, insertParametersValuesInSql); 98 | 99 | commandText.Append("insert into "); 100 | tree.Target.Expression.Accept(translator); 101 | 102 | // Actually is not possible to retrieve the last inserted guid from Access 103 | // We can understand if there is a guid checking the returning value of the insert 104 | // statement 105 | string guidAutogeneratedColumn = null; 106 | if (tree.Returning is DbNewInstanceExpression) 107 | guidAutogeneratedColumn = GetGuidArgs(tree.Returning as DbNewInstanceExpression); 108 | 109 | if (tree.SetClauses.Count != 0) 110 | { 111 | bool first = true; 112 | 113 | // (c1, c2, c3, ...) 114 | commandText.Append("("); 115 | if (!string.IsNullOrEmpty(guidAutogeneratedColumn)) 116 | { 117 | commandText.Append(SQLiteProviderManifestHelper.QuoteIdentifier(guidAutogeneratedColumn)); 118 | first = false; 119 | } 120 | 121 | foreach (DbSetClause setClause in tree.SetClauses) 122 | { 123 | if (first) 124 | first = false; 125 | else 126 | commandText.Append(", "); 127 | 128 | setClause.Property.Accept(translator); 129 | } 130 | commandText.AppendLine(")"); 131 | 132 | // values c1, c2, ... 133 | first = true; 134 | commandText.Append("values ("); 135 | if (!string.IsNullOrEmpty(guidAutogeneratedColumn)) 136 | { 137 | _lastGuid = Guid.NewGuid(); 138 | commandText.Append(string.Format("{{{0}}}", _lastGuid)); 139 | first = false; 140 | } 141 | 142 | foreach (DbSetClause setClause in tree.SetClauses) 143 | { 144 | if (first) 145 | first = false; 146 | else 147 | commandText.Append(", "); 148 | 149 | setClause.Value.Accept(translator); 150 | 151 | translator.RegisterMemberValue(setClause.Property, setClause.Value); 152 | } 153 | commandText.AppendLine(");"); 154 | } 155 | else 156 | commandText.AppendLine(" default values;"); 157 | 158 | // generate returning sql 159 | GenerateReturningSql(commandText, tree, translator, tree.Returning); 160 | 161 | parameters = translator.Parameters; 162 | return commandText.ToString(); 163 | } 164 | 165 | 166 | private static string GetGuidArgs(DbNewInstanceExpression returning) 167 | { 168 | return (from arg in returning.Arguments 169 | where ((DbPropertyExpression)arg).Property.IsStoreGeneratedIdentity 170 | && ((DbPropertyExpression)arg).Property.TypeUsage.EdmType.Name == "guid" 171 | select ((DbPropertyExpression)arg).Property.Name 172 | ).FirstOrDefault() ?? string.Empty; 173 | } 174 | 175 | /// 176 | /// Generates SQL fragment returning server-generated values. 177 | /// Requires: translator knows about member values so that we can figure out 178 | /// how to construct the key predicate. 179 | /// 180 | /// Sample SQL: 181 | /// 182 | /// select IdentityValue 183 | /// from dbo.MyTable 184 | /// where IdentityValue = @@Identity 185 | /// 186 | /// 187 | /// 188 | /// Builder containing command text 189 | /// Modification command tree 190 | /// Translator used to produce DML SQL statement 191 | /// for the tree 192 | /// Returning expression. If null, the method returns 193 | /// immediately without producing a SELECT statement. 194 | private static void GenerateReturningSql(StringBuilder commandText, DbModificationCommandTree tree, 195 | ExpressionTranslator translator, DbExpression returning) 196 | { 197 | // Nothing to do if there is no Returning expression 198 | if (returning == null) { return; } 199 | 200 | // select 201 | commandText.Append("select "); 202 | returning.Accept(translator); 203 | commandText.AppendLine(); 204 | 205 | // from 206 | commandText.Append("from "); 207 | tree.Target.Expression.Accept(translator); 208 | commandText.AppendLine(); 209 | 210 | // where 211 | commandText.Append("where "); 212 | EntitySetBase table = ((DbScanExpression)tree.Target.Expression).Target; 213 | 214 | bool identity = false; 215 | bool first = true; 216 | 217 | foreach (EdmMember keyMember in table.ElementType.KeyMembers) 218 | { 219 | if (first) 220 | first = false; 221 | else 222 | commandText.Append(" and "); 223 | 224 | commandText.Append(SQLiteProviderManifestHelper.QuoteIdentifier(keyMember.Name)); 225 | commandText.Append(" = "); 226 | 227 | // retrieve member value sql. the translator remembers member values 228 | // as it constructs the DML statement (which precedes the "returning" 229 | // SQL) 230 | DbParameter value; 231 | if (translator.MemberValues.TryGetValue(keyMember, out value)) 232 | { 233 | commandText.Append(value.ParameterName); 234 | } 235 | else 236 | { 237 | // if no value is registered for the key member, it means it is an identity 238 | // which can be retrieved using the @@identity function 239 | if (identity) 240 | { 241 | // there can be only one server generated key 242 | throw new NotSupportedException(string.Format("Server generated keys are only supported for identity columns. More than one key column is marked as server generated in table '{0}'.", table.Name)); 243 | } 244 | if (keyMember.TypeUsage.EdmType.Name.ToLower() == "guid") 245 | // We can't retrieve the latest inserted guid from Access 246 | //commandText.Append("@@guid"); 247 | commandText.AppendFormat("{{{0}}}", _lastGuid); 248 | else 249 | commandText.Append("@@identity"); 250 | identity = true; 251 | } 252 | } 253 | } 254 | 255 | /// 256 | /// Lightweight expression translator for DML expression trees, which have constrained 257 | /// scope and support. 258 | /// 259 | private class ExpressionTranslator : DbExpressionVisitor 260 | { 261 | #region UnsupportedVisitMethods 262 | 263 | public override void Visit(DbApplyExpression expression) 264 | { 265 | throw new NotSupportedException("Visit(\"DbApplyExpression\") is not supported."); 266 | } 267 | 268 | public override void Visit(DbArithmeticExpression expression) 269 | { 270 | throw new NotSupportedException("Visit(\"DbArithmeticExpression\") is not supported."); 271 | } 272 | 273 | public override void Visit(DbCaseExpression expression) 274 | { 275 | throw new NotSupportedException("Visit(\"DbCaseExpression\") is not supported."); 276 | } 277 | 278 | public override void Visit(DbCastExpression expression) 279 | { 280 | throw new NotSupportedException("Visit(\"DbCastExpression\") is not supported."); 281 | } 282 | 283 | public override void Visit(DbCrossJoinExpression expression) 284 | { 285 | throw new NotSupportedException("Visit(\"DbCrossJoinExpression\") is not supported."); 286 | } 287 | 288 | public override void Visit(DbDerefExpression expression) 289 | { 290 | throw new NotSupportedException("Visit(\"DbDerefExpression\") is not supported."); 291 | } 292 | 293 | public override void Visit(DbDistinctExpression expression) 294 | { 295 | throw new NotSupportedException("Visit(\"DbDistinctExpression\") is not supported."); 296 | } 297 | 298 | public override void Visit(DbElementExpression expression) 299 | { 300 | throw new NotSupportedException("Visit(\"DbElementExpression\") is not supported."); 301 | } 302 | 303 | public override void Visit(DbEntityRefExpression expression) 304 | { 305 | throw new NotSupportedException("Visit(\"DbEntityRefExpression\") is not supported."); 306 | } 307 | 308 | public override void Visit(DbExceptExpression expression) 309 | { 310 | throw new NotSupportedException("Visit(\"DbExceptExpression\") is not supported."); 311 | } 312 | 313 | public override void Visit(DbExpression expression) 314 | { 315 | throw new NotSupportedException("Visit(\"DbExpression\") is not supported."); 316 | } 317 | 318 | public override void Visit(DbFilterExpression expression) 319 | { 320 | throw new NotSupportedException("Visit(\"DbFilterExpression\") is not supported."); 321 | } 322 | 323 | public override void Visit(DbFunctionExpression expression) 324 | { 325 | throw new NotSupportedException("Visit(\"DbFunctionExpression\") is not supported."); 326 | } 327 | 328 | public override void Visit(DbGroupByExpression expression) 329 | { 330 | throw new NotSupportedException("Visit(\"DbGroupByExpression\") is not supported."); 331 | } 332 | 333 | public override void Visit(DbIntersectExpression expression) 334 | { 335 | throw new NotSupportedException("Visit(\"DbIntersectExpression\") is not supported."); 336 | } 337 | 338 | public override void Visit(DbIsEmptyExpression expression) 339 | { 340 | throw new NotSupportedException("Visit(\"DbIsEmptyExpression\") is not supported."); 341 | } 342 | 343 | public override void Visit(DbIsOfExpression expression) 344 | { 345 | throw new NotSupportedException("Visit(\"DbIsOfExpression\") is not supported."); 346 | } 347 | 348 | public override void Visit(DbJoinExpression expression) 349 | { 350 | throw new NotSupportedException("Visit(\"DbJoinExpression\") is not supported."); 351 | } 352 | 353 | public override void Visit(DbLikeExpression expression) 354 | { 355 | throw new NotSupportedException("Visit(\"DbLikeExpression\") is not supported."); 356 | } 357 | 358 | public override void Visit(DbLimitExpression expression) 359 | { 360 | throw new NotSupportedException("Visit(\"DbLimitExpression\") is not supported."); 361 | } 362 | 363 | public override void Visit(DbOfTypeExpression expression) 364 | { 365 | throw new NotSupportedException("Visit(\"DbOfTypeExpression\") is not supported."); 366 | } 367 | 368 | public override void Visit(DbParameterReferenceExpression expression) 369 | { 370 | throw new NotSupportedException("Visit(\"DbParameterReferenceExpression\") is not supported."); 371 | } 372 | 373 | public override void Visit(DbProjectExpression expression) 374 | { 375 | throw new NotSupportedException("Visit(\"DbProjectExpression\") is not supported."); 376 | } 377 | 378 | public override void Visit(DbQuantifierExpression expression) 379 | { 380 | throw new NotSupportedException("Visit(\"DbQuantifierExpression\") is not supported."); 381 | } 382 | 383 | public override void Visit(DbRefExpression expression) 384 | { 385 | throw new NotSupportedException("Visit(\"DbRefExpression\") is not supported."); 386 | } 387 | 388 | public override void Visit(DbRefKeyExpression expression) 389 | { 390 | throw new NotSupportedException("Visit(\"DbRefKeyExpression\") is not supported."); 391 | } 392 | 393 | public override void Visit(DbRelationshipNavigationExpression expression) 394 | { 395 | throw new NotSupportedException("Visit(\"DbRelationshipNavigationExpression\") is not supported."); 396 | } 397 | 398 | public override void Visit(DbSkipExpression expression) 399 | { 400 | throw new NotSupportedException("Visit(\"DbSkipExpression\") is not supported."); 401 | } 402 | 403 | public override void Visit(DbSortExpression expression) 404 | { 405 | throw new NotSupportedException("Visit(\"DbSortExpression\") is not supported."); 406 | } 407 | 408 | public override void Visit(DbTreatExpression expression) 409 | { 410 | throw new NotSupportedException("Visit(\"DbTreatExpression\") is not supported."); 411 | } 412 | 413 | public override void Visit(DbUnionAllExpression expression) 414 | { 415 | throw new NotSupportedException("Visit(\"DbUnionAllExpression\") is not supported."); 416 | } 417 | 418 | public override void Visit(DbVariableReferenceExpression expression) 419 | { 420 | throw new NotSupportedException("Visit(\"DbVariableReferenceExpression\") is not supported."); 421 | } 422 | 423 | #endregion UnsupportedVisitMethods 424 | 425 | /// 426 | /// Initialize a new expression translator populating the given string builder 427 | /// with command text. Command text builder and command tree must not be null. 428 | /// 429 | /// Command text with which to populate commands 430 | /// Command tree generating SQL 431 | /// Indicates whether the translator should preserve 432 | /// member values while compiling sql expression 433 | /// if set to true parameters values are inserted directly in SQL statement. 434 | internal ExpressionTranslator(StringBuilder commandText, DbModificationCommandTree commandTree, bool preserveMemberValues, bool insertParametersValuesInSql) 435 | { 436 | Debug.Assert(commandText != null); 437 | Debug.Assert(commandTree != null); 438 | 439 | _commandText = commandText; 440 | _parameters = new List(); 441 | _memberValues = preserveMemberValues ? new Dictionary() : null; 442 | _insertParametersValuesInSql = insertParametersValuesInSql; 443 | } 444 | 445 | private readonly StringBuilder _commandText; 446 | private readonly List _parameters; 447 | private readonly Dictionary _memberValues; 448 | private readonly bool _insertParametersValuesInSql; 449 | 450 | private int _parameterNameCount = 0; 451 | 452 | internal List Parameters { get { return _parameters; } } 453 | internal Dictionary MemberValues { get { return _memberValues; } } 454 | 455 | // generate parameter (name based on parameter ordinal) 456 | internal SQLiteParameter CreateParameter(object value, TypeUsage type) 457 | { 458 | var parameter = SQLiteProviderServicesHelper.CreateSQLiteParameter( 459 | string.Concat("@p", _parameterNameCount.ToString(CultureInfo.InvariantCulture)), 460 | type, 461 | ParameterMode.In, 462 | value); 463 | 464 | _parameterNameCount++; 465 | _parameters.Add(parameter); 466 | 467 | return parameter; 468 | } 469 | 470 | public override void Visit(DbAndExpression expression) 471 | { 472 | VisitBinary(expression, " and "); 473 | } 474 | 475 | public override void Visit(DbOrExpression expression) 476 | { 477 | VisitBinary(expression, " or "); 478 | } 479 | 480 | public override void Visit(DbComparisonExpression expression) 481 | { 482 | Debug.Assert(expression.ExpressionKind == DbExpressionKind.Equals, 483 | "only equals comparison expressions are produced in DML command trees in V1"); 484 | 485 | VisitBinary(expression, " = "); 486 | 487 | RegisterMemberValue(expression.Left, expression.Right); 488 | } 489 | 490 | /// 491 | /// Call this method to register a property value pair so the translator "remembers" 492 | /// the values for members of the row being modified. These values can then be used 493 | /// to form a predicate for server-generation (based on the key of the row) 494 | /// 495 | /// DbExpression containing the column reference (property expression). 496 | /// DbExpression containing the value of the column. 497 | internal void RegisterMemberValue(DbExpression propertyExpression, DbExpression value) 498 | { 499 | if (_memberValues != null) 500 | { 501 | // register the value for this property 502 | Debug.Assert(propertyExpression.ExpressionKind == DbExpressionKind.Property, 503 | "DML predicates and setters must be of the form property = value"); 504 | 505 | // get name of left property 506 | EdmMember property = ((DbPropertyExpression)propertyExpression).Property; 507 | 508 | // don't track null values 509 | if (value.ExpressionKind != DbExpressionKind.Null) 510 | { 511 | Debug.Assert(value.ExpressionKind == DbExpressionKind.Constant, 512 | "value must either constant or null"); 513 | // retrieve the last parameter added (which describes the parameter) 514 | _memberValues[property] = _parameters[_parameters.Count - 1]; 515 | } 516 | } 517 | } 518 | 519 | public override void Visit(DbIsNullExpression expression) 520 | { 521 | expression.Argument.Accept(this); 522 | _commandText.Append(" is null"); 523 | } 524 | 525 | public override void Visit(DbNotExpression expression) 526 | { 527 | _commandText.Append("not ("); 528 | expression.Accept(this); 529 | _commandText.Append(")"); 530 | } 531 | 532 | public override void Visit(DbConstantExpression expression) 533 | { 534 | if (_insertParametersValuesInSql) 535 | { 536 | _commandText.Append(VisitConstantExpression(expression)); 537 | } 538 | else 539 | { 540 | SQLiteParameter parameter = CreateParameter(expression.Value, expression.ResultType); 541 | _commandText.Append(parameter.ParameterName); 542 | } 543 | } 544 | 545 | /// 546 | /// Constants will be send to the store as part of the generated SQL statement, not as parameters 547 | /// 548 | /// 549 | /// A . Strings are wrapped in single 550 | /// quotes and escaped. Numbers are written literally. 551 | private string VisitConstantExpression(DbConstantExpression e) 552 | { 553 | return VisitConstantExpression(e.ResultType, e.Value); 554 | } 555 | 556 | private string VisitConstantExpression(TypeUsage expressionType, object expressionValue) 557 | { 558 | StringBuilder result = new StringBuilder(); 559 | 560 | PrimitiveTypeKind typeKind; 561 | // Model Types can be (at the time of this implementation): 562 | // Binary, Boolean, Byte, DateTime, Decimal, Double, Guid, Int16, Int32, Int64,Single, String 563 | if (expressionType.TryGetPrimitiveTypeKind(out typeKind)) 564 | { 565 | switch (typeKind) 566 | { 567 | case PrimitiveTypeKind.Int32: 568 | case PrimitiveTypeKind.Byte: 569 | result.Append(expressionValue); 570 | break; 571 | 572 | case PrimitiveTypeKind.Binary: 573 | result.Append(LiteralHelpers.ToSqlString((Byte[])expressionValue)); 574 | break; 575 | 576 | case PrimitiveTypeKind.Boolean: 577 | result.Append(LiteralHelpers.ToSqlString((bool)expressionValue)); 578 | break; 579 | 580 | 581 | case PrimitiveTypeKind.DateTime: 582 | result.Append(LiteralHelpers.SqlDateTime((System.DateTime)expressionValue)); 583 | break; 584 | 585 | case PrimitiveTypeKind.Time: 586 | result.Append(LiteralHelpers.SqlDayTime((System.DateTime)expressionValue)); 587 | break; 588 | 589 | case PrimitiveTypeKind.DateTimeOffset: 590 | throw new NotImplementedException("Jet does not implement DateTimeOffset"); 591 | 592 | 593 | case PrimitiveTypeKind.Decimal: 594 | string strDecimal = ((Decimal)expressionValue).ToString(CultureInfo.InvariantCulture); 595 | // if the decimal value has no decimal part, cast as decimal to preserve type 596 | // if the number has precision > int64 max precision, it will be handled as decimal by sql server 597 | // and does not need cast. if precision is lest then 20, then cast using Max(literal precision, sql default precision) 598 | if (-1 == strDecimal.IndexOf('.') && (strDecimal.TrimStart(new char[] { '-' }).Length < 20)) 599 | { 600 | byte precision = (Byte)strDecimal.Length; 601 | FacetDescription precisionFacetDescription; 602 | 603 | if (!expressionType.EdmType.TryGetTypeFacetDescriptionByName("precision", out precisionFacetDescription)) 604 | throw new InvalidOperationException("Decimal primitive type must have Precision facet"); 605 | 606 | if (precisionFacetDescription.DefaultValue != null) 607 | precision = Math.Max(precision, (byte)precisionFacetDescription.DefaultValue); 608 | 609 | if (precision <= 0) 610 | throw new InvalidOperationException("Precision must be greater than zero"); 611 | result.Append("cast("); 612 | result.Append(strDecimal); 613 | result.Append(" as decimal("); 614 | result.Append(precision.ToString(CultureInfo.InvariantCulture)); 615 | result.Append("))"); 616 | } 617 | else 618 | { 619 | result.Append(strDecimal); 620 | } 621 | break; 622 | 623 | case PrimitiveTypeKind.Double: 624 | result.Append(((Double)expressionValue).ToString(CultureInfo.InvariantCulture)); 625 | break; 626 | case PrimitiveTypeKind.Single: 627 | result.Append(((Single)expressionValue).ToString(CultureInfo.InvariantCulture)); 628 | break; 629 | case PrimitiveTypeKind.Int16: 630 | case PrimitiveTypeKind.Int64: 631 | result.Append(expressionValue); 632 | break; 633 | case PrimitiveTypeKind.String: 634 | result.Append(LiteralHelpers.ToSqlString(expressionValue as string)); 635 | break; 636 | 637 | case PrimitiveTypeKind.Guid: 638 | result.Append(LiteralHelpers.ToSqlString((Guid)expressionValue)); 639 | break; 640 | 641 | default: 642 | // all known scalar types should been handled already. 643 | throw new NotSupportedException("Primitive type kind " + typeKind + " is not supported by the Jet Provider"); 644 | } 645 | } 646 | else 647 | { 648 | throw new NotSupportedException(); 649 | } 650 | 651 | return result.ToString(); 652 | } 653 | 654 | 655 | public override void Visit(DbScanExpression expression) 656 | { 657 | _commandText.Append(GetTargetTSql(expression.Target)); 658 | } 659 | 660 | public override void Visit(DbPropertyExpression expression) 661 | { 662 | _commandText.Append(SQLiteProviderManifestHelper.QuoteIdentifier(expression.Property.Name)); 663 | } 664 | 665 | public override void Visit(DbNullExpression expression) 666 | { 667 | _commandText.Append("null"); 668 | } 669 | 670 | public override void Visit(DbNewInstanceExpression expression) 671 | { 672 | // assumes all arguments are self-describing (no need to use aliases 673 | // because no renames are ever used in the projection) 674 | bool first = true; 675 | foreach (DbExpression argument in expression.Arguments) 676 | { 677 | if (first) { first = false; } 678 | else { _commandText.Append(", "); } 679 | argument.Accept(this); 680 | } 681 | } 682 | 683 | private void VisitBinary(DbBinaryExpression expression, string separator) 684 | { 685 | _commandText.Append("("); 686 | expression.Left.Accept(this); 687 | _commandText.Append(separator); 688 | expression.Right.Accept(this); 689 | _commandText.Append(")"); 690 | } 691 | } 692 | 693 | /// 694 | /// Gets escaped TSql identifier describing this entity set. 695 | /// 696 | /// 697 | internal static string GetTargetTSql(EntitySetBase entitySetBase) 698 | { 699 | // construct escaped T-SQL referencing entity set 700 | StringBuilder builder = new StringBuilder(50); 701 | string definingQuery = MetadataHelpers.TryGetValueForMetadataProperty(entitySetBase, "DefiningQuery"); 702 | if (!string.IsNullOrEmpty(definingQuery)) 703 | { 704 | builder.Append("("); 705 | builder.Append(definingQuery); 706 | builder.Append(")"); 707 | } 708 | else 709 | { 710 | 711 | string tableName = MetadataHelpers.TryGetValueForMetadataProperty(entitySetBase, "Table"); 712 | if (!string.IsNullOrEmpty(tableName)) 713 | { 714 | builder.Append(SQLiteProviderManifestHelper.QuoteIdentifier(tableName)); 715 | } 716 | else 717 | { 718 | builder.Append(SQLiteProviderManifestHelper.QuoteIdentifier(entitySetBase.Name)); 719 | } 720 | } 721 | return builder.ToString(); 722 | } 723 | } 724 | } 725 | 726 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/SQLiteMigrationSqlGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using System.Data.Entity.Core.Common; 5 | using System.Data.Entity.Core.Common.CommandTrees; 6 | using System.Data.Entity.Core.Metadata.Edm; 7 | using System.Data.Entity.Migrations.Model; 8 | using System.Data.Entity.Migrations.Sql; 9 | using System.Diagnostics; 10 | 11 | namespace System.Data.SQLite.EF6.Migrations 12 | { 13 | /// 14 | /// Migration Ddl generator for SQLite 15 | /// 16 | public class SQLiteMigrationSqlGenerator : MigrationSqlGenerator 17 | { 18 | 19 | const string BATCHTERMINATOR = ";\r\n"; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | public SQLiteMigrationSqlGenerator() 25 | { 26 | base.ProviderManifest = ((DbProviderServices)(new SQLiteProviderFactory()).GetService(typeof(DbProviderServices))).GetProviderManifest(""); 27 | } 28 | 29 | /// 30 | /// Converts a set of migration operations into database provider specific SQL. 31 | /// 32 | /// The operations to be converted. 33 | /// Token representing the version of the database being targeted. 34 | /// 35 | /// A list of SQL statements to be executed to perform the migration operations. 36 | /// 37 | public override IEnumerable Generate(IEnumerable migrationOperations, string providerManifestToken) 38 | { 39 | List migrationStatements = new List(); 40 | 41 | foreach (MigrationOperation migrationOperation in migrationOperations) 42 | migrationStatements.Add(GenerateStatement(migrationOperation)); 43 | return migrationStatements; 44 | } 45 | 46 | private MigrationStatement GenerateStatement(MigrationOperation migrationOperation) 47 | { 48 | MigrationStatement migrationStatement = new MigrationStatement(); 49 | migrationStatement.BatchTerminator = BATCHTERMINATOR; 50 | migrationStatement.Sql = GenerateSqlStatement(migrationOperation); 51 | return migrationStatement; 52 | } 53 | 54 | private string GenerateSqlStatement(MigrationOperation migrationOperation) 55 | { 56 | dynamic concreteMigrationOperation = migrationOperation; 57 | return GenerateSqlStatementConcrete(concreteMigrationOperation); 58 | } 59 | 60 | private string GenerateSqlStatementConcrete(MigrationOperation migrationOperation) 61 | { 62 | if (migrationOperation is SqlOperation sqlOperation) 63 | { 64 | return sqlOperation.Sql; 65 | } 66 | 67 | throw new Exception("Not found SqlOperation"); 68 | } 69 | 70 | 71 | #region History operations 72 | 73 | private string GenerateSqlStatementConcrete(HistoryOperation migrationOperation) 74 | { 75 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 76 | 77 | foreach (DbModificationCommandTree commandTree in migrationOperation.CommandTrees) 78 | { 79 | List parameters; 80 | // Take care because here we have several queries so we can't use parameters... 81 | switch (commandTree.CommandTreeKind) 82 | { 83 | case DbCommandTreeKind.Insert: 84 | ddlBuilder.AppendSql(SQLiteDmlBuilder.GenerateInsertSql((DbInsertCommandTree)commandTree, out parameters, true)); 85 | break; 86 | case DbCommandTreeKind.Delete: 87 | ddlBuilder.AppendSql(SQLiteDmlBuilder.GenerateDeleteSql((DbDeleteCommandTree)commandTree, out parameters, true)); 88 | break; 89 | case DbCommandTreeKind.Update: 90 | ddlBuilder.AppendSql(SQLiteDmlBuilder.GenerateUpdateSql((DbUpdateCommandTree)commandTree, out parameters, true)); 91 | break; 92 | case DbCommandTreeKind.Function: 93 | case DbCommandTreeKind.Query: 94 | default: 95 | throw new InvalidOperationException(string.Format("Command tree of type {0} not supported in migration of history operations", commandTree.CommandTreeKind)); 96 | } 97 | ddlBuilder.AppendSql(BATCHTERMINATOR); 98 | } 99 | 100 | return ddlBuilder.GetCommandText(); 101 | 102 | } 103 | 104 | #endregion 105 | 106 | #region Move operations (not supported by Jet) 107 | 108 | private string GenerateSqlStatementConcrete(MoveProcedureOperation migrationOperation) 109 | { 110 | throw new NotSupportedException("Move operations not supported by SQLite"); 111 | } 112 | 113 | private string GenerateSqlStatementConcrete(MoveTableOperation migrationOperation) 114 | { 115 | throw new NotSupportedException("Move operations not supported by SQLite"); 116 | } 117 | 118 | #endregion 119 | 120 | 121 | #region Procedure related operations (not supported by Jet) 122 | private string GenerateSqlStatementConcrete(AlterProcedureOperation migrationOperation) 123 | { 124 | throw new NotSupportedException("Procedures are not supported by SQLite"); 125 | } 126 | 127 | private string GenerateSqlStatementConcrete(CreateProcedureOperation migrationOperation) 128 | { 129 | throw new NotSupportedException("Procedures are not supported by SQLite"); 130 | } 131 | 132 | 133 | private string GenerateSqlStatementConcrete(DropProcedureOperation migrationOperation) 134 | { 135 | throw new NotSupportedException("Procedures are not supported by SQLite"); 136 | } 137 | 138 | 139 | private string GenerateSqlStatementConcrete(RenameProcedureOperation migrationOperation) 140 | { 141 | throw new NotSupportedException("Procedures are not supported by SQLite"); 142 | } 143 | 144 | #endregion 145 | 146 | 147 | #region Rename operations 148 | 149 | 150 | private string GenerateSqlStatementConcrete(RenameColumnOperation migrationOperation) 151 | { 152 | throw new NotSupportedException("Cannot rename objects with Jet"); 153 | } 154 | 155 | private string GenerateSqlStatementConcrete(RenameIndexOperation migrationOperation) 156 | { 157 | throw new NotSupportedException("Cannot rename objects with Jet"); 158 | } 159 | 160 | private string GenerateSqlStatementConcrete(RenameTableOperation migrationOperation) 161 | { 162 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 163 | 164 | ddlBuilder.AppendSql("ALTER TABLE "); 165 | ddlBuilder.AppendIdentifier(migrationOperation.Name); 166 | ddlBuilder.AppendSql(" RENAME TO "); 167 | ddlBuilder.AppendIdentifier(migrationOperation.NewName); 168 | 169 | return ddlBuilder.GetCommandText(); 170 | } 171 | 172 | #endregion 173 | 174 | #region Columns 175 | private string GenerateSqlStatementConcrete(AddColumnOperation migrationOperation) 176 | { 177 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 178 | 179 | ddlBuilder.AppendSql("ALTER TABLE "); 180 | ddlBuilder.AppendIdentifier(migrationOperation.Table); 181 | ddlBuilder.AppendSql(" ADD COLUMN "); 182 | 183 | ColumnModel column = migrationOperation.Column; 184 | 185 | ddlBuilder.AppendIdentifier(column.Name); 186 | 187 | TypeUsage storeType = ProviderManifest.GetStoreType(column.TypeUsage); 188 | ddlBuilder.AppendType(storeType, column.IsNullable ?? true, column.IsIdentity); 189 | 190 | //DEFAULT VALUE 191 | if (column.DefaultValue != null) 192 | { 193 | if (column.DefaultValue is DateTime defaultValue) 194 | { 195 | var format = "yyyy-MM-dd HH:mm:ss.ffffff"; 196 | if (defaultValue.Kind == DateTimeKind.Utc) format += 'Z'; 197 | ddlBuilder.AppendSql($" DEFAULT '{defaultValue.ToString(format)}'"); 198 | } 199 | } 200 | ddlBuilder.AppendNewLine(); 201 | 202 | return ddlBuilder.GetCommandText(); 203 | } 204 | 205 | 206 | private string GenerateSqlStatementConcrete(DropColumnOperation migrationOperation) 207 | { 208 | throw new NotSupportedException("Drop column not supported by SQLite"); 209 | } 210 | 211 | private string GenerateSqlStatementConcrete(AlterColumnOperation migrationOperation) 212 | { 213 | throw new NotSupportedException("Alter column not supported by SQLite"); 214 | } 215 | 216 | #endregion 217 | 218 | 219 | #region Foreign keys creation 220 | 221 | private string GenerateSqlStatementConcrete(AddForeignKeyOperation migrationOperation) 222 | { 223 | 224 | /* 225 | * SQLite supports foreign key creation only during table creation 226 | * At least, during table creation we could try to create relationships but it 227 | * Requires that we sort tables in dependency order (and that there is not a circular reference 228 | * 229 | * Actually we do not create relationship at all 230 | */ 231 | 232 | return ""; 233 | } 234 | 235 | #endregion 236 | 237 | #region Primary keys creation 238 | 239 | private string GenerateSqlStatementConcrete(AddPrimaryKeyOperation migrationOperation) 240 | { 241 | // Actually primary key creation is supported only during table creation 242 | 243 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 244 | ddlBuilder.AppendSql(" PRIMARY KEY ("); 245 | ddlBuilder.AppendIdentifierList(migrationOperation.Columns); 246 | ddlBuilder.AppendSql(")"); 247 | return ddlBuilder.GetCommandText(); 248 | } 249 | 250 | #endregion 251 | 252 | #region Table operations 253 | 254 | private string GenerateSqlStatementConcrete(AlterTableOperation migrationOperation) 255 | { 256 | /* 257 | * SQLite does not support alter table 258 | * We should rename old table, create the new table, copy old data to new table and drop old table 259 | */ 260 | 261 | throw new NotSupportedException("Alter column not supported by SQLite"); 262 | 263 | } 264 | 265 | private string GenerateSqlStatementConcrete(CreateTableOperation migrationOperation) 266 | { 267 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 268 | 269 | ddlBuilder.AppendSql("CREATE TABLE "); 270 | ddlBuilder.AppendIdentifier(migrationOperation.Name); 271 | ddlBuilder.AppendSql(" ("); 272 | ddlBuilder.AppendNewLine(); 273 | 274 | bool first = true; 275 | string autoincrementColumnName = null; 276 | foreach (ColumnModel column in migrationOperation.Columns) 277 | { 278 | if (first) 279 | first = false; 280 | else 281 | ddlBuilder.AppendSql(","); 282 | 283 | ddlBuilder.AppendSql(" "); 284 | ddlBuilder.AppendIdentifier(column.Name); 285 | ddlBuilder.AppendSql(" "); 286 | if (column.IsIdentity) 287 | { 288 | autoincrementColumnName = column.Name; 289 | ddlBuilder.AppendSql(" integer constraint "); 290 | ddlBuilder.AppendIdentifier(ddlBuilder.CreateConstraintName("PK", migrationOperation.Name)); 291 | ddlBuilder.AppendSql(" primary key autoincrement"); 292 | ddlBuilder.AppendNewLine(); 293 | } 294 | else 295 | { 296 | TypeUsage storeTypeUsage = ProviderManifest.GetStoreType(column.TypeUsage); 297 | ddlBuilder.AppendType(storeTypeUsage, column.IsNullable ?? true, column.IsIdentity); 298 | ddlBuilder.AppendNewLine(); 299 | } 300 | 301 | } 302 | 303 | if (migrationOperation.PrimaryKey != null && autoincrementColumnName == null) 304 | { 305 | ddlBuilder.AppendSql(","); 306 | ddlBuilder.AppendSql(GenerateSqlStatementConcrete(migrationOperation.PrimaryKey)); 307 | } 308 | 309 | ddlBuilder.AppendSql(")"); 310 | 311 | return ddlBuilder.GetCommandText(); 312 | } 313 | 314 | #endregion 315 | 316 | #region Index 317 | 318 | private string GenerateSqlStatementConcrete(CreateIndexOperation migrationOperation) 319 | { 320 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 321 | ddlBuilder.AppendSql("CREATE "); 322 | if (migrationOperation.IsUnique) 323 | ddlBuilder.AppendSql("UNIQUE "); 324 | ddlBuilder.AppendSql("INDEX "); 325 | ddlBuilder.AppendIdentifier(SQLiteProviderManifestHelper.GetFullIdentifierName(migrationOperation.Table, migrationOperation.Name)); 326 | ddlBuilder.AppendSql(" ON "); 327 | ddlBuilder.AppendIdentifier(migrationOperation.Table); 328 | ddlBuilder.AppendSql(" ("); 329 | ddlBuilder.AppendIdentifierList(migrationOperation.Columns); 330 | ddlBuilder.AppendSql(")"); 331 | 332 | return ddlBuilder.GetCommandText(); 333 | } 334 | 335 | #endregion 336 | 337 | #region Drop 338 | 339 | private string GenerateSqlStatementConcrete(DropForeignKeyOperation migrationOperation) 340 | { 341 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 342 | ddlBuilder.AppendSql("ALTER TABLE "); 343 | ddlBuilder.AppendIdentifier(migrationOperation.PrincipalTable); 344 | ddlBuilder.AppendSql(" DROP CONSTRAINT "); 345 | ddlBuilder.AppendIdentifier(migrationOperation.Name); 346 | return ddlBuilder.GetCommandText(); 347 | 348 | } 349 | 350 | private string GenerateSqlStatementConcrete(DropPrimaryKeyOperation migrationOperation) 351 | { 352 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 353 | ddlBuilder.AppendSql("ALTER TABLE "); 354 | ddlBuilder.AppendIdentifier(migrationOperation.Table); 355 | ddlBuilder.AppendSql(" DROP CONSTRAINT "); 356 | ddlBuilder.AppendIdentifier(migrationOperation.Name); 357 | return ddlBuilder.GetCommandText(); 358 | } 359 | 360 | private string GenerateSqlStatementConcrete(DropIndexOperation migrationOperation) 361 | { 362 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 363 | ddlBuilder.AppendSql("DROP INDEX "); 364 | ddlBuilder.AppendIdentifier(SQLiteProviderManifestHelper.GetFullIdentifierName(migrationOperation.Table, migrationOperation.Name)); 365 | ddlBuilder.AppendSql(" ON "); 366 | ddlBuilder.AppendIdentifier(migrationOperation.Table); 367 | return ddlBuilder.GetCommandText(); 368 | } 369 | 370 | private string GenerateSqlStatementConcrete(DropTableOperation migrationOperation) 371 | { 372 | SQLiteDdlBuilder ddlBuilder = new SQLiteDdlBuilder(); 373 | ddlBuilder.AppendSql("DROP TABLE "); 374 | ddlBuilder.AppendIdentifier(migrationOperation.Name); 375 | return ddlBuilder.GetCommandText(); 376 | } 377 | 378 | #endregion 379 | 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/SQLiteProviderManifestHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace System.Data.SQLite.EF6.Migrations 4 | { 5 | static class SQLiteProviderManifestHelper 6 | { 7 | public const int MaxObjectNameLength = 255; 8 | 9 | /// 10 | /// Quotes an identifier 11 | /// 12 | /// Identifier name 13 | /// The quoted identifier 14 | internal static string QuoteIdentifier(string name) 15 | { 16 | if (string.IsNullOrWhiteSpace(name)) 17 | throw new ArgumentNullException("name"); 18 | 19 | return "\"" + name.Replace("\"", "\"\"") + "\""; 20 | } 21 | 22 | 23 | internal static string RemoveDbo(string identifier) 24 | { 25 | if (string.IsNullOrWhiteSpace(identifier)) 26 | throw new ArgumentNullException("identifier"); 27 | 28 | return identifier.ToLower().StartsWith("dbo.") ? identifier.Substring(4) : identifier; 29 | } 30 | 31 | /// 32 | /// Gets the full name of the identifier. 33 | /// 34 | /// Name of the table. 35 | /// Name of the identifier. 36 | /// The full name of the identifier 37 | internal static string GetFullIdentifierName(string tableName, string identifier) 38 | { 39 | return RemoveDbo(tableName) + "_" + identifier; 40 | } 41 | 42 | /// 43 | /// Gets the full name of the identifier. 44 | /// 45 | /// Type prefix to assign basing on the object type. For example PK or FK 46 | /// Name of the table. 47 | /// The identifier. 48 | /// The full name of the identifier 49 | internal static string GetFullIdentifierName(string objectType, string tableName, string identifier) 50 | { 51 | return objectType + "_" + RemoveDbo(tableName) + "_" + identifier; 52 | } 53 | 54 | 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/SQLiteProviderServicesHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity.Core.Metadata.Edm; 3 | using System.Diagnostics; 4 | 5 | namespace System.Data.SQLite.EF6.Migrations 6 | { 7 | static class SQLiteProviderServicesHelper 8 | { 9 | 10 | /// 11 | /// Creates a SQLiteParameter given a name, type, and direction 12 | /// 13 | internal static SQLiteParameter CreateSQLiteParameter(string name, TypeUsage type, ParameterMode mode, object value) 14 | { 15 | int? size; 16 | 17 | if (type.GetPrimitiveTypeKind() == PrimitiveTypeKind.Guid) 18 | { 19 | type = TypeUsage.CreateStringTypeUsage( 20 | PrimitiveType.GetEdmPrimitiveType(PrimitiveTypeKind.String), 21 | false, true); 22 | } 23 | 24 | SQLiteParameter result = new SQLiteParameter(name, value); 25 | 26 | // .Direction 27 | ParameterDirection direction = MetadataHelpers.ParameterModeToParameterDirection(mode); 28 | if (result.Direction != direction) 29 | { 30 | result.Direction = direction; 31 | } 32 | 33 | // .Size and .DbType 34 | // output parameters are handled differently (we need to ensure there is space for return 35 | // values where the user has not given a specific Size/MaxLength) 36 | bool isOutParam = mode != ParameterMode.In; 37 | DbType sqlDbType = GetSQLiteDbType(type, isOutParam, out size); 38 | if (result.DbType != sqlDbType) 39 | { 40 | result.DbType = sqlDbType; 41 | } 42 | 43 | // Note that we overwrite 'facet' parameters where either the value is different or 44 | // there is an output parameter. 45 | if (size.HasValue && (isOutParam || result.Size != size.Value)) 46 | { 47 | result.Size = size.Value; 48 | } 49 | 50 | // .IsNullable 51 | bool isNullable = type.GetIsNullable(); 52 | if (isOutParam || isNullable != result.IsNullable) 53 | { 54 | result.IsNullable = isNullable; 55 | } 56 | 57 | return result; 58 | } 59 | 60 | 61 | /// 62 | /// Determines DbType for the given primitive type. Extracts facet 63 | /// information as well. 64 | /// 65 | private static DbType GetSQLiteDbType(TypeUsage type, bool isOutParam, out int? size) 66 | { 67 | // only supported for primitive type 68 | PrimitiveTypeKind primitiveTypeKind = type.GetPrimitiveTypeKind(); 69 | 70 | size = default(int?); 71 | 72 | switch (primitiveTypeKind) 73 | { 74 | case PrimitiveTypeKind.Binary: 75 | // for output parameters, ensure there is space... 76 | size = GetParameterSize(type, isOutParam); 77 | return GetBinaryDbType(type); 78 | 79 | case PrimitiveTypeKind.Boolean: 80 | return DbType.Boolean; 81 | 82 | case PrimitiveTypeKind.Byte: 83 | return DbType.Byte; 84 | 85 | case PrimitiveTypeKind.Time: 86 | return DbType.Time; 87 | 88 | case PrimitiveTypeKind.DateTimeOffset: 89 | return DbType.DateTimeOffset; 90 | 91 | case PrimitiveTypeKind.DateTime: 92 | return DbType.DateTime; 93 | 94 | case PrimitiveTypeKind.Decimal: 95 | return DbType.Decimal; 96 | 97 | case PrimitiveTypeKind.Double: 98 | return DbType.Double; 99 | 100 | case PrimitiveTypeKind.Guid: 101 | return DbType.Guid; 102 | 103 | case PrimitiveTypeKind.Int16: 104 | return DbType.Int16; 105 | 106 | case PrimitiveTypeKind.Int32: 107 | return DbType.Int32; 108 | 109 | case PrimitiveTypeKind.Int64: 110 | return DbType.Int64; 111 | 112 | case PrimitiveTypeKind.SByte: 113 | return DbType.SByte; 114 | 115 | case PrimitiveTypeKind.Single: 116 | return DbType.Single; 117 | 118 | case PrimitiveTypeKind.String: 119 | size = GetParameterSize(type, isOutParam); 120 | return GetStringDbType(type); 121 | 122 | default: 123 | throw new InvalidOperationException("unknown PrimitiveTypeKind " + primitiveTypeKind); 124 | } 125 | } 126 | 127 | /// 128 | /// Determines preferred value for SQLiteParameter.Size. Returns null 129 | /// where there is no preference. 130 | /// 131 | private static int? GetParameterSize(TypeUsage type, bool isOutParam) 132 | { 133 | int maxLength; 134 | if (type.TryGetMaxLength(out maxLength)) 135 | { 136 | // if the MaxLength facet has a specific value use it 137 | return maxLength; 138 | } 139 | else if (isOutParam) 140 | { 141 | // if the parameter is a return/out/inout parameter, ensure there 142 | // is space for any value 143 | return int.MaxValue; 144 | } 145 | else 146 | { 147 | // no value 148 | return default(int?); 149 | } 150 | } 151 | 152 | /// 153 | /// Chooses the appropriate DbType for the given string type. 154 | /// 155 | private static DbType GetStringDbType(TypeUsage type) 156 | { 157 | Debug.Assert(type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType && 158 | PrimitiveTypeKind.String == ((PrimitiveType)type.EdmType).PrimitiveTypeKind, "only valid for string type"); 159 | 160 | DbType dbType; 161 | 162 | // Specific type depends on whether the string is a unicode string and whether it is a fixed length string. 163 | // By default, assume widest type (unicode) and most common type (variable length) 164 | bool unicode; 165 | bool fixedLength; 166 | if (!type.TryGetIsFixedLength(out fixedLength)) 167 | { 168 | fixedLength = false; 169 | } 170 | 171 | unicode = type.GetIsUnicode(); 172 | 173 | if (fixedLength) 174 | { 175 | dbType = (unicode ? DbType.StringFixedLength : DbType.AnsiStringFixedLength); 176 | } 177 | else 178 | { 179 | dbType = (unicode ? DbType.String : DbType.AnsiString); 180 | } 181 | return dbType; 182 | } 183 | 184 | /// 185 | /// Chooses the appropriate DbType for the given binary type. 186 | /// 187 | private static DbType GetBinaryDbType(TypeUsage type) 188 | { 189 | Debug.Assert(type.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType && 190 | PrimitiveTypeKind.Binary == ((PrimitiveType)type.EdmType).PrimitiveTypeKind, "only valid for binary type"); 191 | 192 | // Specific type depends on whether the binary value is fixed length. By default, assume variable length. 193 | bool fixedLength; 194 | if (!type.TryGetIsFixedLength(out fixedLength)) 195 | // ReSharper disable once RedundantAssignment 196 | fixedLength = false; 197 | 198 | return DbType.Binary; 199 | // return fixedLength ? DbType.Binary : DbType.VarBinary; 200 | } 201 | } 202 | } -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/System.Data.SQLite.EF6.Migrations.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Debug 7 | AnyCPU 8 | {6B715F5A-8F7D-427C-8873-216674B4B401} 9 | Library 10 | Properties 11 | System.Data.SQLite.EF6.Migrations 12 | System.Data.SQLite.EF6.Migrations 13 | v4.5.1 14 | 512 15 | ..\ 16 | true 17 | 18 | 19 | 20 | 21 | 22 | true 23 | full 24 | false 25 | bin\Debug\ 26 | DEBUG;TRACE 27 | prompt 28 | 4 29 | 30 | 31 | pdbonly 32 | true 33 | bin\Release\ 34 | TRACE 35 | prompt 36 | 4 37 | 38 | 39 | 40 | ..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.dll 41 | True 42 | 43 | 44 | ..\packages\EntityFramework.6.3.0\lib\net45\EntityFramework.SqlServer.dll 45 | True 46 | 47 | 48 | 49 | 50 | 51 | ..\packages\System.Data.SQLite.Core.1.0.112.0\lib\net45\System.Data.SQLite.dll 52 | True 53 | 54 | 55 | ..\packages\System.Data.SQLite.EF6.1.0.112.0\lib\net45\System.Data.SQLite.EF6.dll 56 | True 57 | 58 | 59 | ..\packages\System.Data.SQLite.Linq.1.0.112.0\lib\net45\System.Data.SQLite.Linq.dll 60 | True 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/TypeUsageHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data.Entity.Core.Common; 3 | using System.Data.Entity.Core.Metadata.Edm; 4 | 5 | namespace System.Data.SQLite.EF6.Migrations 6 | { 7 | static class TypeUsageHelper 8 | { 9 | public static bool GetIsIdentity(this TypeUsage tu, bool defaultValue = false) 10 | { 11 | Facet storeGenFacet; 12 | if (tu.Facets.TryGetValue("StoreGeneratedPattern", false, out storeGenFacet) && 13 | storeGenFacet.Value != null) 14 | { 15 | StoreGeneratedPattern storeGenPattern = (StoreGeneratedPattern)storeGenFacet.Value; 16 | return storeGenPattern == StoreGeneratedPattern.Identity; 17 | } 18 | return defaultValue; 19 | } 20 | 21 | public static byte GetPrecision(this TypeUsage tu, byte defaultValue = 18) 22 | { 23 | return tu.Facets[DbProviderManifest.PrecisionFacetName] == null ? defaultValue : (byte)tu.Facets[DbProviderManifest.PrecisionFacetName].Value; 24 | } 25 | 26 | public static byte GetScale(this TypeUsage tu, byte defaultValue = 0) 27 | { 28 | return tu.Facets[DbProviderManifest.ScaleFacetName] == null ? defaultValue : (byte)tu.Facets[DbProviderManifest.ScaleFacetName].Value; 29 | } 30 | 31 | public static int GetMaxLength(this TypeUsage tu, int defaultValue = int.MaxValue) 32 | { 33 | return tu.Facets[DbProviderManifest.MaxLengthFacetName].IsUnbounded || tu.Facets[DbProviderManifest.MaxLengthFacetName].Value == null ? defaultValue : (int)tu.Facets[DbProviderManifest.MaxLengthFacetName].Value; 34 | } 35 | 36 | public static bool GetIsFixedLength(this TypeUsage tu, bool defaultValue = false) 37 | { 38 | if (!tu.IsPrimitiveTypeOf(PrimitiveTypeKind.String) && !tu.IsPrimitiveTypeOf(PrimitiveTypeKind.Binary)) 39 | return defaultValue; 40 | 41 | return tu.Facets[DbProviderManifest.FixedLengthFacetName].Value == null ? defaultValue : (bool)tu.Facets[DbProviderManifest.FixedLengthFacetName].Value; 42 | } 43 | 44 | public static bool IsPrimitiveTypeOf(this TypeUsage tu, PrimitiveTypeKind primitiveType) 45 | { 46 | PrimitiveTypeKind typeKind; 47 | if (TryGetPrimitiveTypeKind(tu, out typeKind)) 48 | { 49 | return (typeKind == primitiveType); 50 | } 51 | return false; 52 | } 53 | 54 | public static bool TryGetPrimitiveTypeKind(this TypeUsage tu, out PrimitiveTypeKind typeKind) 55 | { 56 | if (tu != null && tu.EdmType != null && tu.EdmType.BuiltInTypeKind == BuiltInTypeKind.PrimitiveType) 57 | { 58 | typeKind = ((PrimitiveType)tu.EdmType).PrimitiveTypeKind; 59 | return true; 60 | } 61 | 62 | typeKind = default(PrimitiveTypeKind); 63 | return false; 64 | } 65 | 66 | public static PrimitiveTypeKind GetPrimitiveTypeKind(this TypeUsage tu) 67 | { 68 | PrimitiveTypeKind returnValue; 69 | if (!TryGetPrimitiveTypeKind(tu, out returnValue)) 70 | throw new ArgumentException("The type is not a primitive type kind", "tu"); 71 | return returnValue; 72 | } 73 | 74 | 75 | public static bool GetIsUnicode(this TypeUsage tu) 76 | { 77 | return tu.Facets[DbProviderManifest.UnicodeFacetName].Value == null || (bool)tu.Facets[DbProviderManifest.UnicodeFacetName].Value; 78 | } 79 | 80 | public static bool GetPreserveSeconds(this TypeUsage tu) 81 | { 82 | return tu.Facets["PreserveSeconds"].Value != null && (bool)tu.Facets["PreserveSeconds"].Value; 83 | } 84 | 85 | public static bool GetIsNullable(this TypeUsage tu) 86 | { 87 | return tu.Facets[DbProviderManifest.NullableFacetName].Value != null && (bool)tu.Facets[DbProviderManifest.NullableFacetName].Value; 88 | } 89 | 90 | public static bool TryGetPrecision(this TypeUsage tu, out byte precision) 91 | { 92 | Facet f; 93 | 94 | precision = 0; 95 | if (tu.Facets.TryGetValue(DbProviderManifest.PrecisionFacetName, false, out f)) 96 | { 97 | if (!f.IsUnbounded && f.Value != null) 98 | { 99 | precision = (byte)f.Value; 100 | return true; 101 | } 102 | } 103 | return false; 104 | } 105 | 106 | public static bool TryGetMaxLength(this TypeUsage tu, out int maxLength) 107 | { 108 | 109 | maxLength = 0; 110 | 111 | if (!tu.IsPrimitiveTypeOf(PrimitiveTypeKind.String) && !tu.IsPrimitiveTypeOf(PrimitiveTypeKind.Binary)) 112 | return false; 113 | 114 | Facet f; 115 | if (tu.Facets.TryGetValue(DbProviderManifest.MaxLengthFacetName, false, out f)) 116 | { 117 | if (!f.IsUnbounded && f.Value != null) 118 | { 119 | maxLength = (int)f.Value; 120 | return true; 121 | } 122 | } 123 | return false; 124 | } 125 | 126 | public static bool TryGetScale(this TypeUsage tu, out byte scale) 127 | { 128 | Facet f; 129 | 130 | scale = 0; 131 | if (tu.Facets.TryGetValue(DbProviderManifest.ScaleFacetName, false, out f)) 132 | { 133 | if (!f.IsUnbounded && f.Value != null) 134 | { 135 | scale = (byte)f.Value; 136 | return true; 137 | } 138 | } 139 | return false; 140 | } 141 | 142 | public static bool TryGetIsFixedLength(this TypeUsage tu, out bool isFixedLength) 143 | { 144 | isFixedLength = false; 145 | 146 | if (!tu.IsPrimitiveTypeOf(PrimitiveTypeKind.String) && 147 | !tu.IsPrimitiveTypeOf(PrimitiveTypeKind.Binary)) 148 | { 149 | return false; 150 | } 151 | 152 | Facet f; 153 | if (!tu.Facets.TryGetValue(DbProviderManifest.FixedLengthFacetName, true, out f)) 154 | return false; 155 | else 156 | return f.Value == null ? false : (bool)f.Value; 157 | } 158 | 159 | /// 160 | /// Tries to get the name of a facet starting from an EdmType. 161 | /// 162 | /// Type of the edm. 163 | /// Name of the facet. 164 | /// The facet description. 165 | /// True if the facet was found; otherwise false 166 | public static bool TryGetTypeFacetDescriptionByName(this EdmType edmType, string facetName, out FacetDescription facetDescription) 167 | { 168 | facetDescription = null; 169 | if (MetadataHelpers.IsPrimitiveType(edmType)) 170 | { 171 | PrimitiveType primitiveType = (PrimitiveType)edmType; 172 | foreach (FacetDescription fd in primitiveType.FacetDescriptions) 173 | { 174 | if (facetName.Equals(fd.FacetName, StringComparison.OrdinalIgnoreCase)) 175 | { 176 | facetDescription = fd; 177 | return true; 178 | } 179 | } 180 | } 181 | return false; 182 | } 183 | 184 | 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /System.Data.SQLite.EF6.Migrations/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | **Project Description** 2 | Migrations for Entity Framework 6 SQLite provider 3 | 4 | **Limitations:** 5 | - Relationships are not enforced with constraints 6 | - There can be only one identity column per table and will be created as integer and primary key (other primary keys will be ignored) 7 | - It supports only SQLite database file and does not work with in SQLite memory database. 8 | 9 | **How to use it** 10 | - Download the library (using NuGet) 11 | - Create a migration configuration 12 | - Setup the migration configuration (usually during first context creation) 13 | 14 | _Example_ 15 | 16 | ```c# 17 | class Context : DbContext 18 | { 19 | static Context() 20 | { 21 | Database.SetInitializer(new MigrateDatabaseToLatestVersion(true)); 22 | } 23 | 24 | // DbSets 25 | } 26 | 27 | internal sealed class ContextMigrationConfiguration : DbMigrationsConfiguration 28 | { 29 | public ContextMigrationConfiguration() 30 | { 31 | AutomaticMigrationsEnabled = true; 32 | AutomaticMigrationDataLossAllowed = true; 33 | SetSqlGenerator("System.Data.SQLite", new SQLiteMigrationSqlGenerator()); 34 | } 35 | } 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/license.md: -------------------------------------------------------------------------------- 1 | Ms-PL 2 | Microsoft Public License (Ms-PL) 3 | 4 | This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. 5 | 6 | 1. Definitions 7 | 8 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. 9 | 10 | A "contribution" is the original software, or any additions or changes to the software. 11 | 12 | A "contributor" is any person that distributes its contribution under this license. 13 | 14 | "Licensed patents" are a contributor's patent claims that read directly on its contribution. 15 | 16 | 2. Grant of Rights 17 | 18 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. 19 | 20 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. 21 | 22 | 3. Conditions and Limitations 23 | 24 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. 25 | 26 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. 27 | 28 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. 29 | 30 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. 31 | 32 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. --------------------------------------------------------------------------------