├── .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.
--------------------------------------------------------------------------------