diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..0901f69
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: msallin # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
diff --git a/.gitignore b/.gitignore
index f8c1427..1ae906a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -155,7 +155,6 @@ ClientBin/
*~
*.dbmdl
*.dbproj.schemaview
-*.pfx
*.publishsettings
node_modules/
@@ -184,3 +183,4 @@ FakesAssemblies/
# Custom
*.GhostDoc.xml
+/.vs/*
diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config
index 67f8ea0..3f0e003 100644
--- a/.nuget/NuGet.Config
+++ b/.nuget/NuGet.Config
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe
deleted file mode 100644
index 8dd7e45..0000000
Binary files a/.nuget/NuGet.exe and /dev/null differ
diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets
deleted file mode 100644
index 3f8c37b..0000000
--- a/.nuget/NuGet.targets
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
-
- $(MSBuildProjectDirectory)\..\
-
-
- false
-
-
- false
-
-
- true
-
-
- false
-
-
-
-
-
-
-
-
-
-
- $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
-
-
-
-
- $(SolutionDir).nuget
-
-
-
- $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
- $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
-
-
-
- $(MSBuildProjectDirectory)\packages.config
- $(PackagesProjectConfig)
-
-
-
-
- $(NuGetToolsPath)\NuGet.exe
- @(PackageSource)
-
- "$(NuGetExePath)"
- mono --runtime=v4.0.30319 "$(NuGetExePath)"
-
- $(TargetDir.Trim('\\'))
-
- -RequireConsent
- -NonInteractive
-
- "$(SolutionDir) "
- "$(SolutionDir)"
-
-
- $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
- $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
-
-
-
- RestorePackages;
- $(BuildDependsOn);
-
-
-
-
- $(BuildDependsOn);
- BuildPackage;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/BuildAllConfigurations.proj b/BuildAllConfigurations.proj
deleted file mode 100644
index 3a31715..0000000
--- a/BuildAllConfigurations.proj
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- %(PlatformList.Identity)
-
-
-
-
-
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index e06d208..44b57ce 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@ Apache License
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright {yyyy} {name of copyright owner}
+ Copyright 2015 Marc Sallin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/Lib/Moq.dll b/Lib/Moq.dll
deleted file mode 100644
index f5b1545..0000000
Binary files a/Lib/Moq.dll and /dev/null differ
diff --git a/README.md b/README.md
index 5058ff8..63d7eb2 100644
--- a/README.md
+++ b/README.md
@@ -1,46 +1,77 @@
# SQLite CodeFirst
+
**Release Build** [](https://ci.appveyor.com/project/msallin/sqlitecodefirst-nv6vn/branch/master)
**CI Build** [](https://ci.appveyor.com/project/msallin/sqlitecodefirst)
Creates a [SQLite Database](https://sqlite.org/) from Code, using [Entity Framework](https://msdn.microsoft.com/en-us/data/ef.aspx) CodeFirst.
-This Project ships several `IDbInitializer` classes. These create new SQLite Databases based on your model/code.
-I started with the [code](https://gist.github.com/flaub/1968486e1b3f2b9fddaf) from [flaub](https://github.com/flaub).
+## Support the project
+
+To support this project you can: *star the repository*, report bugs/request features by creating new issues, write code and create PRs or donate.
+Especially if you use it for a commercial project, a donation is welcome.
+If you need a specific feature for a commercial project, I am glad to offer a paid implementation.
+
+## Project Status
+
+This project was started when there was .NET Full Framework and EF 6. EF 6 does not offer code first for SQLite and this library fills this gab.
+Nowadays there is .NET Core (or now just called .NET) and EF Core. EF Core supports code first and migrations for SQLite.
+If you use .NET Core 3 or above together with EF Core, there is no need for this library.
+If you use EF 6 (either with .NET Full Framework or with .NET Core 3 or above), this library is an option for you to get code first for SQLite.
+I'm going to maintain this library as long as it is useful for some people (see [History](https://github.com/msallin/SQLiteCodeFirst/issues/166) & [Project Status and Release Schedule](https://github.com/msallin/SQLiteCodeFirst/issues/157)).
+
+## Features
+
+This project ships several `IDbInitializer` classes. These create new SQLite Databases based on your model/code.
+
+The following features are supported:
-Currently the following is supported:
- Tables from classes (supported annotations: `Table`)
- Columns from properties (supported annotations: `Column`, `Key`, `MaxLength`, `Required`, `NotMapped`, `DatabaseGenerated`, `Index`)
- PrimaryKey constraint (`Key` annotation, key composites are supported)
- ForeignKey constraint (1-n relationships, support for 'Cascade on delete')
- Not Null constraint
-- Auto increment (An int PrimaryKey will automatically be incremented)
-- Index (Decorate columns with the `Index` attribute. Indices are automatically created for foreign keys by default. To prevent this you can remove the convetion `ForeignKeyIndexConvention`)
-
-I tried to write the code in a extensible way.
-The logic is divided into two main parts, Builder and Statement.
-The Builder knows how to translate the EdmModel into statements where a statement class creates the SQLite-DDL-Code.
-The structure of the statements is influenced by the [SQLite Language Specification](https://www.sqlite.org/lang.html).
-You will find an extensive usage of the composite pattern.
+- Auto increment (An int PrimaryKey will automatically be incremented and you can explicit set the "AUTOINCREMENT" constraint to a PrimaryKey using the Autoincrement-Attribute)
+- Index (Decorate columns with the `Index` attribute. Indices are automatically created for foreign keys by default. To prevent this you can remove the convention `ForeignKeyIndexConvention`)
+- Unique constraint (Decorate columns with the `UniqueAttribute`, which is part of this library)
+- Collate constraint (Decorate columns with the `CollateAttribute`, which is part of this library. Use `CollationFunction.Custom` to specify a own collation function.)
+- Default collation (pass an instance of Collation as constructor parameter for an initializer to specify a default collation).
+- SQL default value (Decorate columns with the `SqlDefaultValueAttribute`, which is part of this library)
## Install
+
Either get the assembly from the latest [GitHub Release Page](https://github.com/msallin/SQLiteCodeFirst/releases) or install the NuGet-Package [SQLite.CodeFirst](https://www.nuget.org/packages/SQLite.CodeFirst/) (`PM> Install-Package SQLite.CodeFirst`).
-The project is built to target.NET framework versions 4.0 and 4.5.
+The project is built to target .NET framework versions 4.0 and 4.5 and .NET Standard 2.1.
You can use the SQLite CodeFirst in projects that target the following frameworks:
-- .NET 4.0 (use net40)
-- .NET 4.5 (use net45)
-- .NET 4.5.1 (use net45)
-- .NET 4.5.2 (use net45)
+
+- .NET 4.0 (uses net40)
+- .NET 4.5-4.8 (uses net45)
+- .NET Core 3.0-3.1 (uses netstandard2.1)
+- .NET 5-8 (uses netstandard2.1)
## How to use
+
+The functionality is exposed by using implementations of the `IDbInitializer<>` interface.
+Depending on your need, you can choose from the following initializers:
+
+- SqliteCreateDatabaseIfNotExists
+- SqliteDropCreateDatabaseAlways
+- SqliteDropCreateDatabaseWhenModelChanges
+
+If you want to have more control, you can use the `SqliteDatabaseCreator` (implements `IDatabaseCreator`) which lets you control the creation of the SQLite database.
+Or for even more control, use the `SqliteSqlGenerator` (implements `ISqlGenerator`), which lets you generate the SQL code based on your `EdmModel`.
+
When you want to let the Entity Framework create database if it does not exist, just set `SqliteDropCreateDatabaseAlways<>` or `SqliteCreateDatabaseIfNotExists<>` as your `IDbInitializer<>`.
+
+### Initializer Sample
+
```csharp
public class MyDbContext : DbContext
{
public MyDbContext()
: base("ConnectionStringName") { }
-
+
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var sqliteConnectionInitializer = new SqliteCreateDatabaseIfNotExists(modelBuilder);
@@ -49,9 +80,14 @@ public class MyDbContext : DbContext
}
```
+Notice that the `SqliteDropCreateDatabaseWhenModelChanges<>` initializer will create a additional table in your database.
+This table is used to store some information to detect model changes. If you want to use an own entity/table you have to implement the
+`IHistory` interface and pass the type of your entity as parameter to the constructor of the initializer.
+
In a more advanced scenario, you may want to populate some core- or test-data after the database was created.
-To do this, inherit from `SqliteDropCreateDatabaseAlways<>` or `SqliteCreateDatabaseIfNotExists<>` and override the `Seed(MyDbContext context)` function.
-This function will be called in a transaction once the database was created. This function is only executed if a new database was successfully created.
+To do this, inherit from `SqliteDropCreateDatabaseAlways<>`, `SqliteCreateDatabaseIfNotExists<>` or `SqliteDropCreateDatabaseWhenModelChanges<>` and override the `Seed(MyDbContext context)` function.
+This function will be called in a transaction, once the database was created. This function is only executed if a new database was successfully created.
+
```csharp
public class MyDbContextInitializer : SqliteDropCreateDatabaseAlways
{
@@ -65,6 +101,85 @@ public class MyDbContextInitializer : SqliteDropCreateDatabaseAlways
+
+```
+
+Add the following class.
+```csharp
+public class MyConfiguration : DbConfiguration, IDbConnectionFactory {
+ public MyConfiguration()
+ {
+ SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance);
+ SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
+
+ var providerServices = (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices));
+
+ SetProviderServices("System.Data.SQLite", providerServices);
+ SetProviderServices("System.Data.SQLite.EF6", providerServices);
+
+ SetDefaultConnectionFactory(this);
+ }
+
+ public DbConnection CreateConnection(string connectionString)
+ => new SQLiteConnection(connectionString);
+ }
+}
+```
+
+Also, make sure you specify the DbConfigurationType on the DBContext class as well
+
+```csharp
+[DbConfigurationType(typeof(MyConfiguration))]
+public class Context: DbContext {
+ //... DBContext things
+}
+```
+## Structure
+
+The code is written in an extensible way.
+The logic is divided into two main parts, Builder and Statement.
+The Builder knows how to translate the EdmModel into statements where a statement class creates the SQLite-DDL-Code.
+The structure of the statements is influenced by the [SQLite Language Specification](https://www.sqlite.org/lang.html).
+You will find an extensive usage of the composite pattern.
+
## Hints
+
If you try to reinstall the NuGet-Packages (e.g. if you want to downgrade to .NET 4.0), the app.config will be overwritten and you may getting an exception when you try to run the console project.
-In this case please check the following issue: https://github.com/msallin/SQLiteCodeFirst/issues/13.
+In this case please check the following issue:
+
+## Recognition
+
+I started with the [code](https://gist.github.com/flaub/1968486e1b3f2b9fddaf) from [flaub](https://github.com/flaub).
diff --git a/SQLite.CodeFirst.Console/App.config b/SQLite.CodeFirst.Console/App.config
index b7d5048..9fa9559 100644
--- a/SQLite.CodeFirst.Console/App.config
+++ b/SQLite.CodeFirst.Console/App.config
@@ -11,9 +11,10 @@
-
-
+
+
+
@@ -25,6 +26,7 @@
+
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/CustomHistory.cs b/SQLite.CodeFirst.Console/Entity/CustomHistory.cs
new file mode 100644
index 0000000..51ad92c
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/CustomHistory.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace SQLite.CodeFirst.Console.Entity
+{
+ public class CustomHistory : IHistory
+ {
+ public int Id { get; set; }
+ public string Hash { get; set; }
+ public string Context { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.Console/Entity/Foo.cs b/SQLite.CodeFirst.Console/Entity/Foo.cs
new file mode 100644
index 0000000..85fa6d9
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/Foo.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class Foo
+ {
+ private ICollection _fooSelves;
+ private ICollection _fooSteps;
+
+ public int FooId { get; set; }
+ public string Name { get; set; }
+ public int? FooSelf1Id { get; set; }
+ public int? FooSelf2Id { get; set; }
+ public int? FooSelf3Id { get; set; }
+
+ [ForeignKey("FooSelf1Id")]
+ public virtual Foo ParentMyEntity1 { get; set; }
+
+ [ForeignKey("FooSelf2Id")]
+ public virtual Foo ParentMyEntity2 { get; set; }
+
+ [ForeignKey("FooSelf3Id")]
+ public virtual Foo ParentMyEntity3 { get; set; }
+
+ public virtual ICollection FooSteps
+ {
+ get { return _fooSteps ?? (_fooSteps = new HashSet()); }
+ set { _fooSteps = value; }
+ }
+
+ public virtual ICollection FooSelves
+ {
+ get { return _fooSelves ?? (_fooSelves = new HashSet()); }
+ set { _fooSelves = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs b/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs
new file mode 100644
index 0000000..2301500
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooCompositeKey
+ {
+ [Key, Column(Order = 1)]
+ public int Id { get; set; }
+
+ [Key, Column(Order = 2), StringLength(20)]
+ public string Version { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection FooeyACollection { get; set; }
+
+ public virtual ICollection FooeyBCollection { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/FooRelationshipA.cs b/SQLite.CodeFirst.Console/Entity/FooRelationshipA.cs
new file mode 100644
index 0000000..38baaf2
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/FooRelationshipA.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipA
+ {
+ public int Id { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection Fooey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/FooRelationshipB.cs b/SQLite.CodeFirst.Console/Entity/FooRelationshipB.cs
new file mode 100644
index 0000000..271d8f8
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/FooRelationshipB.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipB
+ {
+ public int Id { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection Fooey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/FooSelf.cs b/SQLite.CodeFirst.Console/Entity/FooSelf.cs
new file mode 100644
index 0000000..0268835
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/FooSelf.cs
@@ -0,0 +1,13 @@
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class FooSelf
+ {
+ public int FooSelfId { get; set; }
+ public int FooId { get; set; }
+ public int Number { get; set; }
+ public virtual Foo Foo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/FooStep.cs b/SQLite.CodeFirst.Console/Entity/FooStep.cs
new file mode 100644
index 0000000..aa0139f
--- /dev/null
+++ b/SQLite.CodeFirst.Console/Entity/FooStep.cs
@@ -0,0 +1,13 @@
+namespace SQLite.CodeFirst.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class FooStep
+ {
+ public int FooStepId { get; set; }
+ public int FooId { get; set; }
+ public int Number { get; set; }
+ public virtual Foo Foo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/IEntity.cs b/SQLite.CodeFirst.Console/Entity/IEntity.cs
index e88d169..17afb3b 100644
--- a/SQLite.CodeFirst.Console/Entity/IEntity.cs
+++ b/SQLite.CodeFirst.Console/Entity/IEntity.cs
@@ -1,6 +1,6 @@
namespace SQLite.CodeFirst.Console.Entity
{
- interface IEntity
+ public interface IEntity
{
int Id { get; set; }
}
diff --git a/SQLite.CodeFirst.Console/Entity/Person.cs b/SQLite.CodeFirst.Console/Entity/Person.cs
index acae0e9..8bc807b 100644
--- a/SQLite.CodeFirst.Console/Entity/Person.cs
+++ b/SQLite.CodeFirst.Console/Entity/Person.cs
@@ -1,4 +1,6 @@
-using System.ComponentModel.DataAnnotations;
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
namespace SQLite.CodeFirst.Console.Entity
{
@@ -7,6 +9,7 @@ public abstract class Person : IEntity
public int Id { get; set; }
[MaxLength(50)]
+ [Collate(CollationFunction.NoCase)]
public string FirstName { get; set; }
[MaxLength(50)]
@@ -17,5 +20,9 @@ public abstract class Person : IEntity
[Required]
public string City { get; set; }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
+ [SqlDefaultValue(DefaultValue = "DATETIME('now')")]
+ public DateTime CreatedUtc { get; set; }
}
}
diff --git a/SQLite.CodeFirst.Console/Entity/Player.cs b/SQLite.CodeFirst.Console/Entity/Player.cs
index 8e69fab..5ec5be6 100644
--- a/SQLite.CodeFirst.Console/Entity/Player.cs
+++ b/SQLite.CodeFirst.Console/Entity/Player.cs
@@ -6,8 +6,16 @@ namespace SQLite.CodeFirst.Console.Entity
public class Player : Person
{
[Index] // Automatically named 'IX_TeamPlayer_Number'
+ [Index("IX_TeamPlayer_NumberPerTeam", Order = 1, IsUnique = true)]
public int Number { get; set; }
+ // The index attribute must be placed on the FK not on the navigation property (team).
+ [Index("IX_TeamPlayer_NumberPerTeam", Order = 2, IsUnique = true)]
+ public int TeamId { get; set; }
+
+ // Its not possible to set an index on this property. Use the FK property (teamId).
public virtual Team Team { get; set; }
+
+ public virtual Player Mentor { get; set; }
}
}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Entity/Stadion.cs b/SQLite.CodeFirst.Console/Entity/Stadion.cs
index e9a49a8..d77dd2b 100644
--- a/SQLite.CodeFirst.Console/Entity/Stadion.cs
+++ b/SQLite.CodeFirst.Console/Entity/Stadion.cs
@@ -7,16 +7,20 @@ public class Stadion
{
[Key]
[Column(Order = 1)]
- [Index("IX_Stadion_Main", Order = 2)] // Test for combined, named index
+ [Index("IX_Stadion_Main", Order = 2, IsUnique = true)] // Test for combined, named index
public string Name { get; set; }
[Key]
[Column(Order = 2)]
- [Index("IX_Stadion_Main", Order = 1)] // Test for combined, named index
+ [Index("IX_Stadion_Main", Order = 1, IsUnique = true)] // Test for combined, named index
public string Street { get; set; }
[Key]
[Column(Order = 3)]
public string City { get; set; }
+
+ [Column(Order = 4)]
+ [Index("ReservedKeyWordTest", IsUnique = true)]
+ public int Order { get; set; }
}
}
diff --git a/SQLite.CodeFirst.Console/Entity/Team.cs b/SQLite.CodeFirst.Console/Entity/Team.cs
index 5a5a795..905be57 100644
--- a/SQLite.CodeFirst.Console/Entity/Team.cs
+++ b/SQLite.CodeFirst.Console/Entity/Team.cs
@@ -6,6 +6,7 @@ namespace SQLite.CodeFirst.Console.Entity
{
public class Team : IEntity
{
+ [Autoincrement]
public int Id { get; set; }
[Index("IX_Team_TeamsName")] // Test for named index.
diff --git a/SQLite.CodeFirst.Console/FootballDbContext.cs b/SQLite.CodeFirst.Console/FootballDbContext.cs
index 6f0ed9e..77b9178 100644
--- a/SQLite.CodeFirst.Console/FootballDbContext.cs
+++ b/SQLite.CodeFirst.Console/FootballDbContext.cs
@@ -1,111 +1,33 @@
-using System.Collections.Generic;
+using System.Data.Common;
using System.Data.Entity;
-using System.Data.Entity.ModelConfiguration.Conventions;
-using SQLite.CodeFirst.Console.Entity;
namespace SQLite.CodeFirst.Console
{
public class FootballDbContext : DbContext
{
- public FootballDbContext()
- : base("footballDb")
+ public FootballDbContext(string nameOrConnectionString)
+ : base(nameOrConnectionString)
{
- Configuration.ProxyCreationEnabled = true;
- Configuration.LazyLoadingEnabled = true;
+ Configure();
}
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ public FootballDbContext(DbConnection connection, bool contextOwnsConnection)
+ : base(connection, contextOwnsConnection)
{
- modelBuilder.Conventions.Remove();
-
- ConfigureTeamEntity(modelBuilder);
- ConfigureStadionEntity(modelBuilder);
- ConfigureCoachEntity(modelBuilder);
- ConfigurePlayerEntity(modelBuilder);
-
- var initializer = new FootballDbInitializer(modelBuilder);
- Database.SetInitializer(initializer);
+ Configure();
}
- private static void ConfigureTeamEntity(DbModelBuilder modelBuilder)
+ private void Configure()
{
- modelBuilder.Entity().ToTable("Base.MyTable")
- .HasRequired(t => t.Coach)
- .WithMany()
- .WillCascadeOnDelete(false);
-
- modelBuilder.Entity()
- .HasRequired(t => t.Stadion)
- .WithRequiredPrincipal()
- .WillCascadeOnDelete(true);
- }
-
- private static void ConfigureStadionEntity(DbModelBuilder modelBuilder)
- {
- modelBuilder.Entity();
- }
-
- private static void ConfigureCoachEntity(DbModelBuilder modelBuilder)
- {
- modelBuilder.Entity()
- .HasRequired(p => p.Team)
- .WithRequiredPrincipal(t => t.Coach)
- .WillCascadeOnDelete(false);
- }
-
- private static void ConfigurePlayerEntity(DbModelBuilder modelBuilder)
- {
- modelBuilder.Entity()
- .HasRequired(p => p.Team)
- .WithMany(team => team.Players)
- .WillCascadeOnDelete(true);
+ Configuration.ProxyCreationEnabled = true;
+ Configuration.LazyLoadingEnabled = true;
}
- }
-
- public class FootballDbInitializer : SqliteDropCreateDatabaseAlways
- {
- public FootballDbInitializer(DbModelBuilder modelBuilder)
- : base(modelBuilder)
- { }
- protected override void Seed(FootballDbContext context)
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
- context.Set().Add(new Team
- {
- Name = "YB",
- Coach = new Coach
- {
- City = "Zürich",
- FirstName = "Masssaman",
- LastName = "Nachn",
- Street = "Testingstreet 844"
- },
- Players = new List
- {
- new Player
- {
- City = "Bern",
- FirstName = "Marco",
- LastName = "Bürki",
- Street = "Wunderstrasse 43",
- Number = 12
- },
- new Player
- {
- City = "Berlin",
- FirstName = "Alain",
- LastName = "Rochat",
- Street = "Wonderstreet 13",
- Number = 14
- }
- },
- Stadion = new Stadion
- {
- Name = "Stade de Suisse",
- City = "Bern",
- Street = "Papiermühlestrasse 71"
- }
- });
+ ModelConfiguration.Configure(modelBuilder);
+ var initializer = new FootballDbInitializer(modelBuilder);
+ Database.SetInitializer(initializer);
}
}
-}
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/FootballDbInitializer.cs b/SQLite.CodeFirst.Console/FootballDbInitializer.cs
new file mode 100644
index 0000000..59e9aa6
--- /dev/null
+++ b/SQLite.CodeFirst.Console/FootballDbInitializer.cs
@@ -0,0 +1,17 @@
+using System.Data.Entity;
+using SQLite.CodeFirst.Console.Entity;
+
+namespace SQLite.CodeFirst.Console
+{
+ public class FootballDbInitializer : SqliteDropCreateDatabaseWhenModelChanges
+ {
+ public FootballDbInitializer(DbModelBuilder modelBuilder)
+ : base(modelBuilder, typeof(CustomHistory))
+ { }
+
+ protected override void Seed(FootballDbContext context)
+ {
+ // Here you can seed your core data if you have any.
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/ModelConfiguration.cs b/SQLite.CodeFirst.Console/ModelConfiguration.cs
new file mode 100644
index 0000000..30703ad
--- /dev/null
+++ b/SQLite.CodeFirst.Console/ModelConfiguration.cs
@@ -0,0 +1,66 @@
+using System.Data.Entity;
+using SQLite.CodeFirst.Console.Entity;
+
+namespace SQLite.CodeFirst.Console
+{
+ public class ModelConfiguration
+ {
+ public static void Configure(DbModelBuilder modelBuilder)
+ {
+ ConfigureTeamEntity(modelBuilder);
+ ConfigureStadionEntity(modelBuilder);
+ ConfigureCoachEntity(modelBuilder);
+ ConfigurePlayerEntity(modelBuilder);
+ ConfigureSelfReferencingEntities(modelBuilder);
+ ConfigureCompositeKeyEntities(modelBuilder);
+ }
+
+ private static void ConfigureTeamEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().ToTable("Base.MyTable")
+ .HasRequired(t => t.Coach)
+ .WithMany()
+ .WillCascadeOnDelete(false);
+
+ modelBuilder.Entity()
+ .HasRequired(t => t.Stadion)
+ .WithRequiredPrincipal()
+ .WillCascadeOnDelete(true);
+ }
+
+ private static void ConfigureStadionEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ }
+
+ private static void ConfigureCoachEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasRequired(p => p.Team)
+ .WithRequiredPrincipal(t => t.Coach)
+ .WillCascadeOnDelete(false);
+ }
+
+ private static void ConfigurePlayerEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasRequired(p => p.Team)
+ .WithMany(team => team.Players)
+ .WillCascadeOnDelete(true);
+ }
+
+ private static void ConfigureSelfReferencingEntities(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ }
+
+ private static void ConfigureCompositeKeyEntities(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Console/Program.cs b/SQLite.CodeFirst.Console/Program.cs
index 79641ca..d3913c4 100644
--- a/SQLite.CodeFirst.Console/Program.cs
+++ b/SQLite.CodeFirst.Console/Program.cs
@@ -1,34 +1,107 @@
-using SQLite.CodeFirst.Console.Entity;
+using System.Collections.Generic;
+using System.Data.Entity;
+using System.Data.SQLite;
+using System.Linq;
+using SQLite.CodeFirst.Console.Entity;
namespace SQLite.CodeFirst.Console
{
public static class Program
{
- static void Main()
+ private static void Main()
{
- System.Console.WriteLine("Starting Demo Application");
+ StartDemoUseInMemory();
+ StartDemoUseFile();
+ PressEnterToExit();
+ }
- var context = CreateAndSeedDatabase();
+ private static void StartDemoUseInMemory()
+ {
+ System.Console.WriteLine("Starting Demo Application (In Memory)");
+ System.Console.WriteLine(string.Empty);
- DisplaySeededData(context);
+ using (var sqLiteConnection = new SQLiteConnection("data source=:memory:"))
+ {
+ // This is required if a in memory db is used.
+ sqLiteConnection.Open();
- PressEnterToExit();
+ using (var context = new FootballDbContext(sqLiteConnection, false))
+ {
+ CreateAndSeedDatabase(context);
+ DisplaySeededData(context);
+ }
+ }
}
- private static FootballDbContext CreateAndSeedDatabase()
+ private static void StartDemoUseFile()
+ {
+ System.Console.WriteLine("Starting Demo Application (File)");
+ System.Console.WriteLine(string.Empty);
+
+ using (var context = new FootballDbContext("footballDb"))
+ {
+ CreateAndSeedDatabase(context);
+ DisplaySeededData(context);
+ }
+ }
+
+ private static void CreateAndSeedDatabase(DbContext context)
{
System.Console.WriteLine("Create and seed the database.");
- var context = new FootballDbContext();
+
+ if (context.Set().Count() != 0)
+ {
+ return;
+ }
+
+ context.Set().Add(new Team
+ {
+ Name = "YB",
+ Coach = new Coach
+ {
+ City = "Zürich",
+ FirstName = "Masssaman",
+ LastName = "Nachn",
+ Street = "Testingstreet 844"
+ },
+ Players = new List
+ {
+ new Player
+ {
+ City = "Bern",
+ FirstName = "Marco",
+ LastName = "Bürki",
+ Street = "Wunderstrasse 43",
+ Number = 12
+ },
+ new Player
+ {
+ City = "Berlin",
+ FirstName = "Alain",
+ LastName = "Rochat",
+ Street = "Wonderstreet 13",
+ Number = 14
+ }
+ },
+ Stadion = new Stadion
+ {
+ Name = "Stade de Suisse",
+ City = "Bern",
+ Street = "Papiermühlestrasse 71"
+ }
+ });
+
+ context.SaveChanges();
+
System.Console.WriteLine("Completed.");
System.Console.WriteLine();
- return context;
}
- private static void DisplaySeededData(FootballDbContext context)
+ private static void DisplaySeededData(DbContext context)
{
System.Console.WriteLine("Display seeded data.");
- foreach (var team in context.Set())
+ foreach (Team team in context.Set())
{
System.Console.WriteLine("\t Team:");
System.Console.WriteLine("\t Id: {0}", team.Id);
@@ -49,7 +122,7 @@ private static void DisplaySeededData(FootballDbContext context)
System.Console.WriteLine("\t\t City: {0}", team.Coach.City);
System.Console.WriteLine();
- foreach (var player in team.Players)
+ foreach (Player player in team.Players)
{
System.Console.WriteLine("\t\t Player:");
System.Console.WriteLine("\t\t Id: {0}", player.Id);
@@ -58,6 +131,7 @@ private static void DisplaySeededData(FootballDbContext context)
System.Console.WriteLine("\t\t LastName: {0}", player.LastName);
System.Console.WriteLine("\t\t Street: {0}", player.Street);
System.Console.WriteLine("\t\t City: {0}", player.City);
+ System.Console.WriteLine("\t\t Created: {0}", player.CreatedUtc);
System.Console.WriteLine();
}
}
@@ -70,4 +144,4 @@ private static void PressEnterToExit()
System.Console.ReadLine();
}
}
-}
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/Properties/AssemblyInfo.cs b/SQLite.CodeFirst.Console/Properties/AssemblyInfo.cs
deleted file mode 100644
index 02804aa..0000000
--- a/SQLite.CodeFirst.Console/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-[assembly: AssemblyTitle("SQLite.CodeFirst")]
-[assembly: AssemblyDescription("A console application which demonstrates how to use SQLite.CodeFirst.")]
-[assembly: AssemblyProduct("SQLite.CodeFirst")]
-[assembly: AssemblyCopyright("Copyright © Marc Sallin")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("945f6a68-1bcd-47ff-a551-a2820b88ff8c")]
-
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: AssemblyInformationalVersion("1.0.0.0")]
diff --git a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
index b04e945..0ba463e 100644
--- a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
+++ b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
@@ -1,104 +1,24 @@
-
-
-
+
- Debug
- AnyCPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}
Exe
- Properties
- SQLite.CodeFirst.Console
- SQLite.CodeFirst.Console
- v4.5.2
- 512
- true
-
-
- ..\
- true
-
-
- AnyCPU
- true
- full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
-
-
+ net48
+ SQLite.CodeFirst
+ A console application which demonstrates how to use SQLite.CodeFirst.
+ 1.0.0.0
AnyCPU
- pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
+ true
+ ..\Shared\SQLite.CodeFirst.StrongNameKey.snk
-
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
-
-
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
-
-
+
+
+
-
-
- ..\packages\System.Data.SQLite.Core.1.0.97.0\lib\net451\System.Data.SQLite.dll
- True
-
-
- ..\packages\System.Data.SQLite.EF6.1.0.97.0\lib\net451\System.Data.SQLite.EF6.dll
-
-
- ..\packages\System.Data.SQLite.Linq.1.0.97.0\lib\net451\System.Data.SQLite.Linq.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {50A32FE4-0E13-4213-A373-72523CDF34D9}
- SQLite.CodeFirst
-
+
-
-
-
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Console/packages.config b/SQLite.CodeFirst.Console/packages.config
deleted file mode 100644
index b4537bc..0000000
--- a/SQLite.CodeFirst.Console/packages.config
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Configuration.cs b/SQLite.CodeFirst.NetCore.Console/Configuration.cs
new file mode 100644
index 0000000..845f483
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Configuration.cs
@@ -0,0 +1,28 @@
+using System.Data.Common;
+using System.Data.Entity;
+using System.Data.Entity.Core.Common;
+using System.Data.Entity.Infrastructure;
+using System.Data.SQLite;
+using System.Data.SQLite.EF6;
+
+namespace SQLite.CodeFirst.NetCore.Console
+{
+ public class Configuration : DbConfiguration, IDbConnectionFactory
+ {
+ public Configuration()
+ {
+ SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance);
+ SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
+
+ var providerServices = (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices));
+
+ SetProviderServices("System.Data.SQLite", providerServices);
+ SetProviderServices("System.Data.SQLite.EF6", providerServices);
+
+ SetDefaultConnectionFactory(this);
+ }
+
+ public DbConnection CreateConnection(string connectionString)
+ => new SQLiteConnection(connectionString);
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Coach.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Coach.cs
new file mode 100644
index 0000000..6dfc65b
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Coach.cs
@@ -0,0 +1,7 @@
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public class Coach : Person
+ {
+ public virtual Team Team { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/CustomHistory.cs b/SQLite.CodeFirst.NetCore.Console/Entity/CustomHistory.cs
new file mode 100644
index 0000000..740c3da
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/CustomHistory.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public class CustomHistory : IHistory
+ {
+ public int Id { get; set; }
+ public string Hash { get; set; }
+ public string Context { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Foo.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Foo.cs
new file mode 100644
index 0000000..f5a1bdd
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Foo.cs
@@ -0,0 +1,41 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class Foo
+ {
+ private ICollection _fooSelves;
+ private ICollection _fooSteps;
+
+ public int FooId { get; set; }
+ public string Name { get; set; }
+ public int? FooSelf1Id { get; set; }
+ public int? FooSelf2Id { get; set; }
+ public int? FooSelf3Id { get; set; }
+
+ [ForeignKey("FooSelf1Id")]
+ public virtual Foo ParentMyEntity1 { get; set; }
+
+ [ForeignKey("FooSelf2Id")]
+ public virtual Foo ParentMyEntity2 { get; set; }
+
+ [ForeignKey("FooSelf3Id")]
+ public virtual Foo ParentMyEntity3 { get; set; }
+
+ public virtual ICollection FooSteps
+ {
+ get { return _fooSteps ?? (_fooSteps = new HashSet()); }
+ set { _fooSteps = value; }
+ }
+
+ public virtual ICollection FooSelves
+ {
+ get { return _fooSelves ?? (_fooSelves = new HashSet()); }
+ set { _fooSelves = value; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/FooCompositeKey.cs b/SQLite.CodeFirst.NetCore.Console/Entity/FooCompositeKey.cs
new file mode 100644
index 0000000..b4bb2d6
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/FooCompositeKey.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooCompositeKey
+ {
+ [Key, Column(Order = 1)]
+ public int Id { get; set; }
+
+ [Key, Column(Order = 2), StringLength(20)]
+ public string Version { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection FooeyACollection { get; set; }
+
+ public virtual ICollection FooeyBCollection { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipA.cs b/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipA.cs
new file mode 100644
index 0000000..f733cd7
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipA.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipA
+ {
+ public int Id { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection Fooey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipB.cs b/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipB.cs
new file mode 100644
index 0000000..3b75f68
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/FooRelationshipB.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipB
+ {
+ public int Id { get; set; }
+
+ [StringLength(255)]
+ public string Name { get; set; }
+
+ public virtual ICollection Fooey { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/FooSelf.cs b/SQLite.CodeFirst.NetCore.Console/Entity/FooSelf.cs
new file mode 100644
index 0000000..6a8ad39
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/FooSelf.cs
@@ -0,0 +1,13 @@
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class FooSelf
+ {
+ public int FooSelfId { get; set; }
+ public int FooId { get; set; }
+ public int Number { get; set; }
+ public virtual Foo Foo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/FooStep.cs b/SQLite.CodeFirst.NetCore.Console/Entity/FooStep.cs
new file mode 100644
index 0000000..b13b3a4
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/FooStep.cs
@@ -0,0 +1,13 @@
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/69 and https://github.com/msallin/SQLiteCodeFirst/issues/63
+ ///
+ public class FooStep
+ {
+ public int FooStepId { get; set; }
+ public int FooId { get; set; }
+ public int Number { get; set; }
+ public virtual Foo Foo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/IEntity.cs b/SQLite.CodeFirst.NetCore.Console/Entity/IEntity.cs
new file mode 100644
index 0000000..9a97b92
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/IEntity.cs
@@ -0,0 +1,7 @@
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public interface IEntity
+ {
+ int Id { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Person.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Person.cs
new file mode 100644
index 0000000..e5ebda4
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Person.cs
@@ -0,0 +1,28 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public abstract class Person : IEntity
+ {
+ public int Id { get; set; }
+
+ [MaxLength(50)]
+ [Collate(CollationFunction.NoCase)]
+ public string FirstName { get; set; }
+
+ [MaxLength(50)]
+ public string LastName { get; set; }
+
+ [MaxLength(100)]
+ public string Street { get; set; }
+
+ [Required]
+ public string City { get; set; }
+
+ [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
+ [SqlDefaultValue(DefaultValue = "DATETIME('now')")]
+ public DateTime CreatedUtc { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Player.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Player.cs
new file mode 100644
index 0000000..f642654
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Player.cs
@@ -0,0 +1,21 @@
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ [Table("TeamPlayer")]
+ public class Player : Person
+ {
+ [Index] // Automatically named 'IX_TeamPlayer_Number'
+ [Index("IX_TeamPlayer_NumberPerTeam", Order = 1, IsUnique = true)]
+ public int Number { get; set; }
+
+ // The index attribute must be placed on the FK not on the navigation property (team).
+ [Index("IX_TeamPlayer_NumberPerTeam", Order = 2, IsUnique = true)]
+ public int TeamId { get; set; }
+
+ // Its not possible to set an index on this property. Use the FK property (teamId).
+ public virtual Team Team { get; set; }
+
+ public virtual Player Mentor { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Stadion.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Stadion.cs
new file mode 100644
index 0000000..d8a2c50
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Stadion.cs
@@ -0,0 +1,26 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public class Stadion
+ {
+ [Key]
+ [Column(Order = 1)]
+ [Index("IX_Stadion_Main", Order = 2, IsUnique = true)] // Test for combined, named index
+ public string Name { get; set; }
+
+ [Key]
+ [Column(Order = 2)]
+ [Index("IX_Stadion_Main", Order = 1, IsUnique = true)] // Test for combined, named index
+ public string Street { get; set; }
+
+ [Key]
+ [Column(Order = 3)]
+ public string City { get; set; }
+
+ [Column(Order = 4)]
+ [Index("ReservedKeyWordTest", IsUnique = true)]
+ public int Order { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Entity/Team.cs b/SQLite.CodeFirst.NetCore.Console/Entity/Team.cs
new file mode 100644
index 0000000..d448a5f
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Entity/Team.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst.NetCore.Console.Entity
+{
+ public class Team : IEntity
+ {
+ [Autoincrement]
+ public int Id { get; set; }
+
+ [Index("IX_Team_TeamsName")] // Test for named index.
+ [Required]
+ public string Name { get; set; }
+
+ public virtual Coach Coach { get; set; }
+
+ public virtual ICollection Players { get; set; }
+
+ public virtual Stadion Stadion { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/FootballDbContext.cs b/SQLite.CodeFirst.NetCore.Console/FootballDbContext.cs
new file mode 100644
index 0000000..330c6c0
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/FootballDbContext.cs
@@ -0,0 +1,33 @@
+using System.Data.Common;
+using System.Data.Entity;
+
+namespace SQLite.CodeFirst.NetCore.Console
+{
+ public class FootballDbContext : DbContext
+ {
+ public FootballDbContext(string nameOrConnectionString)
+ : base(nameOrConnectionString)
+ {
+ Configure();
+ }
+
+ public FootballDbContext(DbConnection connection, bool contextOwnsConnection)
+ : base(connection, contextOwnsConnection)
+ {
+ Configure();
+ }
+
+ private void Configure()
+ {
+ Configuration.ProxyCreationEnabled = true;
+ Configuration.LazyLoadingEnabled = true;
+ }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ ModelConfiguration.Configure(modelBuilder);
+ var initializer = new FootballDbInitializer(modelBuilder);
+ Database.SetInitializer(initializer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/FootballDbInitializer.cs b/SQLite.CodeFirst.NetCore.Console/FootballDbInitializer.cs
new file mode 100644
index 0000000..0d4a426
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/FootballDbInitializer.cs
@@ -0,0 +1,17 @@
+using System.Data.Entity;
+using SQLite.CodeFirst.NetCore.Console.Entity;
+
+namespace SQLite.CodeFirst.NetCore.Console
+{
+ public class FootballDbInitializer : SqliteDropCreateDatabaseWhenModelChanges
+ {
+ public FootballDbInitializer(DbModelBuilder modelBuilder)
+ : base(modelBuilder, typeof(CustomHistory))
+ { }
+
+ protected override void Seed(FootballDbContext context)
+ {
+ // Here you can seed your core data if you have any.
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.NetCore.Console/ModelConfiguration.cs b/SQLite.CodeFirst.NetCore.Console/ModelConfiguration.cs
new file mode 100644
index 0000000..254f6f8
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/ModelConfiguration.cs
@@ -0,0 +1,66 @@
+using System.Data.Entity;
+using SQLite.CodeFirst.NetCore.Console.Entity;
+
+namespace SQLite.CodeFirst.NetCore.Console
+{
+ public class ModelConfiguration
+ {
+ public static void Configure(DbModelBuilder modelBuilder)
+ {
+ ConfigureTeamEntity(modelBuilder);
+ ConfigureStadionEntity(modelBuilder);
+ ConfigureCoachEntity(modelBuilder);
+ ConfigurePlayerEntity(modelBuilder);
+ ConfigureSelfReferencingEntities(modelBuilder);
+ ConfigureCompositeKeyEntities(modelBuilder);
+ }
+
+ private static void ConfigureTeamEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().ToTable("Base.MyTable")
+ .HasRequired(t => t.Coach)
+ .WithMany()
+ .WillCascadeOnDelete(false);
+
+ modelBuilder.Entity()
+ .HasRequired(t => t.Stadion)
+ .WithRequiredPrincipal()
+ .WillCascadeOnDelete(true);
+ }
+
+ private static void ConfigureStadionEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ }
+
+ private static void ConfigureCoachEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasRequired(p => p.Team)
+ .WithRequiredPrincipal(t => t.Coach)
+ .WillCascadeOnDelete(false);
+ }
+
+ private static void ConfigurePlayerEntity(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasRequired(p => p.Team)
+ .WithMany(team => team.Players)
+ .WillCascadeOnDelete(true);
+ }
+
+ private static void ConfigureSelfReferencingEntities(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ }
+
+ private static void ConfigureCompositeKeyEntities(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/Program.cs b/SQLite.CodeFirst.NetCore.Console/Program.cs
new file mode 100644
index 0000000..3bd2b42
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/Program.cs
@@ -0,0 +1,147 @@
+using System.Collections.Generic;
+using System.Data.Entity;
+using System.Data.SQLite;
+using System.Linq;
+using SQLite.CodeFirst.NetCore.Console.Entity;
+
+namespace SQLite.CodeFirst.NetCore.Console
+{
+ public static class Program
+ {
+ private static void Main()
+ {
+ StartDemoUseInMemory();
+ StartDemoUseFile();
+ PressEnterToExit();
+ }
+
+ private static void StartDemoUseInMemory()
+ {
+ System.Console.WriteLine("Starting Demo Application (In Memory)");
+ System.Console.WriteLine(string.Empty);
+
+ using (var sqLiteConnection = new SQLiteConnection("data source=:memory:"))
+ {
+ // This is required if a in memory db is used.
+ sqLiteConnection.Open();
+
+ using (var context = new FootballDbContext(sqLiteConnection, false))
+ {
+ CreateAndSeedDatabase(context);
+ DisplaySeededData(context);
+ }
+ }
+ }
+
+ private static void StartDemoUseFile()
+ {
+ System.Console.WriteLine("Starting Demo Application (File)");
+ System.Console.WriteLine(string.Empty);
+
+ using (var context = new FootballDbContext(@"data source=.\db\footballDb\footballDb.sqlite;foreign keys=true"))
+ {
+ CreateAndSeedDatabase(context);
+ DisplaySeededData(context);
+ }
+ }
+
+ private static void CreateAndSeedDatabase(DbContext context)
+ {
+ System.Console.WriteLine("Create and seed the database.");
+
+ if (context.Set().Count() != 0)
+ {
+ return;
+ }
+
+ context.Set().Add(new Team
+ {
+ Name = "YB",
+ Coach = new Coach
+ {
+ City = "Zürich",
+ FirstName = "Masssaman",
+ LastName = "Nachn",
+ Street = "Testingstreet 844"
+ },
+ Players = new List
+ {
+ new Player
+ {
+ City = "Bern",
+ FirstName = "Marco",
+ LastName = "Bürki",
+ Street = "Wunderstrasse 43",
+ Number = 12
+ },
+ new Player
+ {
+ City = "Berlin",
+ FirstName = "Alain",
+ LastName = "Rochat",
+ Street = "Wonderstreet 13",
+ Number = 14
+ }
+ },
+ Stadion = new Stadion
+ {
+ Name = "Stade de Suisse",
+ City = "Bern",
+ Street = "Papiermühlestrasse 71"
+ }
+ });
+
+ context.SaveChanges();
+
+ System.Console.WriteLine("Completed.");
+ System.Console.WriteLine();
+ }
+
+ private static void DisplaySeededData(DbContext context)
+ {
+ System.Console.WriteLine("Display seeded data.");
+
+ foreach (Team team in context.Set())
+ {
+ System.Console.WriteLine("\t Team:");
+ System.Console.WriteLine("\t Id: {0}", team.Id);
+ System.Console.WriteLine("\t Name: {0}", team.Name);
+ System.Console.WriteLine();
+
+ System.Console.WriteLine("\t\t Stadion:");
+ System.Console.WriteLine("\t\t Name: {0}", team.Stadion.Name);
+ System.Console.WriteLine("\t\t Street: {0}", team.Stadion.Street);
+ System.Console.WriteLine("\t\t City: {0}", team.Stadion.City);
+ System.Console.WriteLine();
+
+ System.Console.WriteLine("\t\t Coach:");
+ System.Console.WriteLine("\t\t Id: {0}", team.Coach.Id);
+ System.Console.WriteLine("\t\t FirstName: {0}", team.Coach.FirstName);
+ System.Console.WriteLine("\t\t LastName: {0}", team.Coach.LastName);
+ System.Console.WriteLine("\t\t Street: {0}", team.Coach.Street);
+ System.Console.WriteLine("\t\t City: {0}", team.Coach.City);
+ System.Console.WriteLine();
+
+ foreach (Player player in team.Players)
+ {
+ System.Console.WriteLine("\t\t Player:");
+ System.Console.WriteLine("\t\t Id: {0}", player.Id);
+ System.Console.WriteLine("\t\t Number: {0}", player.Number);
+ System.Console.WriteLine("\t\t FirstName: {0}", player.FirstName);
+ System.Console.WriteLine("\t\t LastName: {0}", player.LastName);
+ System.Console.WriteLine("\t\t Street: {0}", player.Street);
+ System.Console.WriteLine("\t\t City: {0}", player.City);
+ System.Console.WriteLine("\t\t Created: {0}", player.CreatedUtc);
+ System.Console.WriteLine();
+ }
+ }
+ }
+
+ private static void PressEnterToExit()
+ {
+ System.Console.WriteLine();
+ System.Console.WriteLine("Press 'Enter' to exit.");
+ System.Console.ReadLine();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.NetCore.Console/SQLite.CodeFirst.NetCore.Console.csproj b/SQLite.CodeFirst.NetCore.Console/SQLite.CodeFirst.NetCore.Console.csproj
new file mode 100644
index 0000000..5b2fa34
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/SQLite.CodeFirst.NetCore.Console.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs
new file mode 100644
index 0000000..c707341
--- /dev/null
+++ b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs
@@ -0,0 +1,64 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Console;
+using SQLite.CodeFirst.Console.Entity;
+
+namespace SQLite.CodeFirst.Test.IntegrationTests
+{
+
+ [TestClass]
+ public class InMemoryDbCreationTest : InMemoryDbTest
+ {
+ [TestMethod]
+ public void CreateInMemoryDatabaseTest()
+ {
+ using (var context = GetDbContext())
+ {
+ context.Set().Add(new Team
+ {
+ Name = "New",
+ Coach = new Coach
+ {
+ City = "New",
+ FirstName = "New",
+ LastName = "New",
+ Street = "New"
+ },
+ Players = new List
+ {
+ new Player
+ {
+ City = "New",
+ FirstName = "New",
+ LastName = "New",
+ Street = "New",
+ Number = 1
+ },
+ new Player
+ {
+ City = "New",
+ FirstName = "New",
+ LastName = "New",
+ Street = "New",
+ Number = 2
+ }
+ },
+ Stadion = new Stadion
+ {
+ Name = "New",
+ City = "New",
+ Street = "New"
+ }
+ });
+
+ context.SaveChanges();
+ }
+
+ using (var context = GetDbContext())
+ {
+ Assert.AreEqual(1, context.Set().Count());
+ }
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbTest.cs
new file mode 100644
index 0000000..314b760
--- /dev/null
+++ b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbTest.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Data.Common;
+using System.Data.Entity;
+using System.Data.SQLite;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace SQLite.CodeFirst.Test.IntegrationTests
+{
+ ///
+ /// Inherit this class to create a test which uses a SQLite in memory database.
+ /// This class provides the necessary logic to run multiple tests again the in memory database in a row.
+ ///
+ public abstract class InMemoryDbTest
+ where TDbContext : DbContext
+ {
+ private bool dbInitialized;
+
+ protected DbConnection Connection { get; private set; }
+
+ protected DbContext GetDbContext()
+ {
+ TDbContext context = (TDbContext)Activator.CreateInstance(typeof(TDbContext), Connection, false);
+ if (!dbInitialized)
+ {
+ context.Database.Initialize(true);
+ dbInitialized = true;
+ }
+ return context;
+ }
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ Connection = new SQLiteConnection("data source=:memory:");
+
+ // This is important! Else the in memory database will not work.
+ Connection.Open();
+
+ dbInitialized = false;
+ }
+
+ [TestCleanup]
+ public void Cleanup()
+ {
+ Connection.Dispose();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationDefaultCollationTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationDefaultCollationTest.cs
new file mode 100644
index 0000000..d6cbd08
--- /dev/null
+++ b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationDefaultCollationTest.cs
@@ -0,0 +1,118 @@
+using System.Data.Common;
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using System.Data.SQLite;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Console;
+using SQLite.CodeFirst.Console.Entity;
+
+namespace SQLite.CodeFirst.Test.IntegrationTests
+{
+ [TestClass]
+ public class SqlGenerationDefaultCollationTest
+ {
+ private const string ReferenceSql =
+ @"
+CREATE TABLE ""MyTable"" ([Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, [Name] nvarchar NOT NULL COLLATE custom_collate, FOREIGN KEY ([Id]) REFERENCES ""Coaches""([Id]));
+CREATE TABLE ""Coaches"" ([Id] INTEGER PRIMARY KEY, [FirstName] nvarchar (50) COLLATE NOCASE, [LastName] nvarchar (50) COLLATE custom_collate, [Street] nvarchar (100) COLLATE custom_collate, [City] nvarchar NOT NULL COLLATE custom_collate, [CreatedUtc] datetime NOT NULL DEFAULT (DATETIME('now')));
+CREATE TABLE ""TeamPlayer"" ([Id] INTEGER PRIMARY KEY, [Number] int NOT NULL, [TeamId] int NOT NULL, [FirstName] nvarchar (50) COLLATE NOCASE, [LastName] nvarchar (50) COLLATE custom_collate, [Street] nvarchar (100) COLLATE custom_collate, [City] nvarchar NOT NULL COLLATE custom_collate, [CreatedUtc] datetime NOT NULL DEFAULT (DATETIME('now')), [Mentor_Id] int, FOREIGN KEY ([Mentor_Id]) REFERENCES ""TeamPlayer""([Id]), FOREIGN KEY ([TeamId]) REFERENCES ""MyTable""([Id]) ON DELETE CASCADE);
+CREATE TABLE ""Stadions"" ([Name] nvarchar (128) NOT NULL COLLATE custom_collate, [Street] nvarchar (128) NOT NULL COLLATE custom_collate, [City] nvarchar (128) NOT NULL COLLATE custom_collate, [Order] int NOT NULL, [Team_Id] int NOT NULL, PRIMARY KEY([Name], [Street], [City]), FOREIGN KEY ([Team_Id]) REFERENCES ""MyTable""([Id]) ON DELETE CASCADE);
+CREATE TABLE ""Foos"" ([FooId] INTEGER PRIMARY KEY, [Name] nvarchar COLLATE custom_collate, [FooSelf1Id] int, [FooSelf2Id] int, [FooSelf3Id] int, FOREIGN KEY ([FooSelf1Id]) REFERENCES ""Foos""([FooId]), FOREIGN KEY ([FooSelf2Id]) REFERENCES ""Foos""([FooId]), FOREIGN KEY ([FooSelf3Id]) REFERENCES ""Foos""([FooId]));
+CREATE TABLE ""FooSelves"" ([FooSelfId] INTEGER PRIMARY KEY, [FooId] int NOT NULL, [Number] int NOT NULL, FOREIGN KEY ([FooId]) REFERENCES ""Foos""([FooId]) ON DELETE CASCADE);
+CREATE TABLE ""FooSteps"" ([FooStepId] INTEGER PRIMARY KEY, [FooId] int NOT NULL, [Number] int NOT NULL, FOREIGN KEY ([FooId]) REFERENCES ""Foos""([FooId]) ON DELETE CASCADE);
+CREATE TABLE ""FooCompositeKeys"" ([Id] int NOT NULL, [Version] nvarchar (20) NOT NULL COLLATE custom_collate, [Name] nvarchar (255) COLLATE custom_collate, PRIMARY KEY([Id], [Version]));
+CREATE TABLE ""FooRelationshipAs"" ([Id] INTEGER PRIMARY KEY, [Name] nvarchar (255) COLLATE custom_collate);
+CREATE TABLE ""FooRelationshipBs"" ([Id] INTEGER PRIMARY KEY, [Name] nvarchar (255) COLLATE custom_collate);
+CREATE TABLE ""FooRelationshipAFooCompositeKeys"" ([FooRelationshipA_Id] int NOT NULL, [FooCompositeKey_Id] int NOT NULL, [FooCompositeKey_Version] nvarchar (20) NOT NULL COLLATE custom_collate, PRIMARY KEY([FooRelationshipA_Id], [FooCompositeKey_Id], [FooCompositeKey_Version]), FOREIGN KEY ([FooRelationshipA_Id]) REFERENCES ""FooRelationshipAs""([Id]) ON DELETE CASCADE, FOREIGN KEY ([FooCompositeKey_Id], [FooCompositeKey_Version]) REFERENCES ""FooCompositeKeys""([Id], [Version]) ON DELETE CASCADE);
+CREATE TABLE ""FooRelationshipBFooCompositeKeys"" ([FooRelationshipB_Id] int NOT NULL, [FooCompositeKey_Id] int NOT NULL, [FooCompositeKey_Version] nvarchar (20) NOT NULL COLLATE custom_collate, PRIMARY KEY([FooRelationshipB_Id], [FooCompositeKey_Id], [FooCompositeKey_Version]), FOREIGN KEY ([FooRelationshipB_Id]) REFERENCES ""FooRelationshipBs""([Id]) ON DELETE CASCADE, FOREIGN KEY ([FooCompositeKey_Id], [FooCompositeKey_Version]) REFERENCES ""FooCompositeKeys""([Id], [Version]) ON DELETE CASCADE);
+CREATE INDEX ""IX_MyTable_Id"" ON ""MyTable"" (""Id"");
+CREATE INDEX ""IX_Team_TeamsName"" ON ""MyTable"" (""Name"");
+CREATE INDEX ""IX_TeamPlayer_Number"" ON ""TeamPlayer"" (""Number"");
+CREATE UNIQUE INDEX ""IX_TeamPlayer_NumberPerTeam"" ON ""TeamPlayer"" (""Number"", ""TeamId"");
+CREATE INDEX ""IX_TeamPlayer_Mentor_Id"" ON ""TeamPlayer"" (""Mentor_Id"");
+CREATE UNIQUE INDEX ""IX_Stadion_Main"" ON ""Stadions"" (""Street"", ""Name"");
+CREATE UNIQUE INDEX ""ReservedKeyWordTest"" ON ""Stadions"" (""Order"");
+CREATE INDEX ""IX_Stadion_Team_Id"" ON ""Stadions"" (""Team_Id"");
+CREATE INDEX ""IX_Foo_FooSelf1Id"" ON ""Foos"" (""FooSelf1Id"");
+CREATE INDEX ""IX_Foo_FooSelf2Id"" ON ""Foos"" (""FooSelf2Id"");
+CREATE INDEX ""IX_Foo_FooSelf3Id"" ON ""Foos"" (""FooSelf3Id"");
+CREATE INDEX ""IX_FooSelf_FooId"" ON ""FooSelves"" (""FooId"");
+CREATE INDEX ""IX_FooStep_FooId"" ON ""FooSteps"" (""FooId"");
+CREATE INDEX ""IX_FooRelationshipAFooCompositeKey_FooRelationshipA_Id"" ON ""FooRelationshipAFooCompositeKeys"" (""FooRelationshipA_Id"");
+CREATE INDEX ""IX_FooRelationshipAFooCompositeKey_FooCompositeKey_Id_FooCompositeKey_Version"" ON ""FooRelationshipAFooCompositeKeys"" (""FooCompositeKey_Id"", ""FooCompositeKey_Version"");
+CREATE INDEX ""IX_FooRelationshipBFooCompositeKey_FooRelationshipB_Id"" ON ""FooRelationshipBFooCompositeKeys"" (""FooRelationshipB_Id"");
+CREATE INDEX ""IX_FooRelationshipBFooCompositeKey_FooCompositeKey_Id_FooCompositeKey_Version"" ON ""FooRelationshipBFooCompositeKeys"" (""FooCompositeKey_Id"", ""FooCompositeKey_Version"");
+";
+
+ private static string generatedSql;
+
+ // Does not work on the build server. No clue why.
+
+ [TestMethod]
+ public void SqliteSqlGeneratorWithDefaultCollationTest()
+ {
+ using (DbConnection connection = new SQLiteConnection("FullUri=file::memory:"))
+ {
+ // This is important! Else the in memory database will not work.
+ connection.Open();
+
+ var defaultCollation = new Collation() { Function = CollationFunction.Custom, CustomFunction = "custom_collate" };
+ using (var context = new DummyDbContext(connection, defaultCollation))
+ {
+ // ReSharper disable once UnusedVariable
+ Player fo = context.Set().FirstOrDefault();
+
+ Assert.AreEqual(RemoveLineEndings(ReferenceSql), RemoveLineEndings(generatedSql));
+ }
+ }
+ }
+
+ private static string RemoveLineEndings(string input)
+ {
+ string lineSeparator = ((char)0x2028).ToString();
+ string paragraphSeparator = ((char)0x2029).ToString();
+ return input.Replace("\r\n", string.Empty).Replace("\n", string.Empty).Replace("\r", string.Empty).Replace(lineSeparator, string.Empty).Replace(paragraphSeparator, string.Empty);
+ }
+
+ private class DummyDbContext : DbContext
+ {
+ private readonly Collation defaultCollation;
+
+ public DummyDbContext(DbConnection connection, Collation defaultCollation = null)
+ : base(connection, false)
+ {
+ this.defaultCollation = defaultCollation;
+ }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ // This configuration contains all supported cases.
+ // So it makes a perfect test to validate whether the
+ // generated SQL is correct.
+ ModelConfiguration.Configure(modelBuilder);
+ var initializer = new AssertInitializer(modelBuilder, defaultCollation);
+ Database.SetInitializer(initializer);
+ }
+
+ private class AssertInitializer : SqliteInitializerBase
+ {
+ private readonly Collation defaultCollation;
+
+ public AssertInitializer(DbModelBuilder modelBuilder, Collation defaultCollation)
+ : base(modelBuilder)
+ {
+ this.defaultCollation = defaultCollation;
+ }
+
+ public override void InitializeDatabase(DummyDbContext context)
+ {
+ DbModel model = ModelBuilder.Build(context.Database.Connection);
+ var sqliteSqlGenerator = new SqliteSqlGenerator(defaultCollation);
+ generatedSql = sqliteSqlGenerator.Generate(model.StoreModel);
+ base.InitializeDatabase(context);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationTest.cs
new file mode 100644
index 0000000..6be0a61
--- /dev/null
+++ b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationTest.cs
@@ -0,0 +1,111 @@
+using System.Data.Common;
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using System.Data.SQLite;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Console;
+using SQLite.CodeFirst.Console.Entity;
+
+namespace SQLite.CodeFirst.Test.IntegrationTests
+{
+ [TestClass]
+ public class SqlGenerationTest
+ {
+ private const string ReferenceSql =
+ @"
+CREATE TABLE ""MyTable"" ([Id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, [Name] nvarchar NOT NULL, FOREIGN KEY ([Id]) REFERENCES ""Coaches""([Id]));
+CREATE TABLE ""Coaches"" ([Id] INTEGER PRIMARY KEY, [FirstName] nvarchar (50) COLLATE NOCASE, [LastName] nvarchar (50), [Street] nvarchar (100), [City] nvarchar NOT NULL, [CreatedUtc] datetime NOT NULL DEFAULT (DATETIME('now')));
+CREATE TABLE ""TeamPlayer"" ([Id] INTEGER PRIMARY KEY, [Number] int NOT NULL, [TeamId] int NOT NULL, [FirstName] nvarchar (50) COLLATE NOCASE, [LastName] nvarchar (50), [Street] nvarchar (100), [City] nvarchar NOT NULL, [CreatedUtc] datetime NOT NULL DEFAULT (DATETIME('now')), [Mentor_Id] int, FOREIGN KEY ([Mentor_Id]) REFERENCES ""TeamPlayer""([Id]), FOREIGN KEY ([TeamId]) REFERENCES ""MyTable""([Id]) ON DELETE CASCADE);
+CREATE TABLE ""Stadions"" ([Name] nvarchar (128) NOT NULL, [Street] nvarchar (128) NOT NULL, [City] nvarchar (128) NOT NULL, [Order] int NOT NULL, [Team_Id] int NOT NULL, PRIMARY KEY([Name], [Street], [City]), FOREIGN KEY ([Team_Id]) REFERENCES ""MyTable""([Id]) ON DELETE CASCADE);
+CREATE TABLE ""Foos"" ([FooId] INTEGER PRIMARY KEY, [Name] nvarchar, [FooSelf1Id] int, [FooSelf2Id] int, [FooSelf3Id] int, FOREIGN KEY ([FooSelf1Id]) REFERENCES ""Foos""([FooId]), FOREIGN KEY ([FooSelf2Id]) REFERENCES ""Foos""([FooId]), FOREIGN KEY ([FooSelf3Id]) REFERENCES ""Foos""([FooId]));
+CREATE TABLE ""FooSelves"" ([FooSelfId] INTEGER PRIMARY KEY, [FooId] int NOT NULL, [Number] int NOT NULL, FOREIGN KEY ([FooId]) REFERENCES ""Foos""([FooId]) ON DELETE CASCADE);
+CREATE TABLE ""FooSteps"" ([FooStepId] INTEGER PRIMARY KEY, [FooId] int NOT NULL, [Number] int NOT NULL, FOREIGN KEY ([FooId]) REFERENCES ""Foos""([FooId]) ON DELETE CASCADE);
+CREATE TABLE ""FooCompositeKeys"" ([Id] int NOT NULL, [Version] nvarchar (20) NOT NULL, [Name] nvarchar (255), PRIMARY KEY([Id], [Version]));
+CREATE TABLE ""FooRelationshipAs"" ([Id] INTEGER PRIMARY KEY, [Name] nvarchar (255));
+CREATE TABLE ""FooRelationshipBs"" ([Id] INTEGER PRIMARY KEY, [Name] nvarchar (255));
+CREATE TABLE ""FooRelationshipAFooCompositeKeys"" ([FooRelationshipA_Id] int NOT NULL, [FooCompositeKey_Id] int NOT NULL, [FooCompositeKey_Version] nvarchar (20) NOT NULL, PRIMARY KEY([FooRelationshipA_Id], [FooCompositeKey_Id], [FooCompositeKey_Version]), FOREIGN KEY ([FooRelationshipA_Id]) REFERENCES ""FooRelationshipAs""([Id]) ON DELETE CASCADE, FOREIGN KEY ([FooCompositeKey_Id], [FooCompositeKey_Version]) REFERENCES ""FooCompositeKeys""([Id], [Version]) ON DELETE CASCADE);
+CREATE TABLE ""FooRelationshipBFooCompositeKeys"" ([FooRelationshipB_Id] int NOT NULL, [FooCompositeKey_Id] int NOT NULL, [FooCompositeKey_Version] nvarchar (20) NOT NULL, PRIMARY KEY([FooRelationshipB_Id], [FooCompositeKey_Id], [FooCompositeKey_Version]), FOREIGN KEY ([FooRelationshipB_Id]) REFERENCES ""FooRelationshipBs""([Id]) ON DELETE CASCADE, FOREIGN KEY ([FooCompositeKey_Id], [FooCompositeKey_Version]) REFERENCES ""FooCompositeKeys""([Id], [Version]) ON DELETE CASCADE);
+CREATE INDEX ""IX_MyTable_Id"" ON ""MyTable"" (""Id"");
+CREATE INDEX ""IX_Team_TeamsName"" ON ""MyTable"" (""Name"");
+CREATE INDEX ""IX_TeamPlayer_Number"" ON ""TeamPlayer"" (""Number"");
+CREATE UNIQUE INDEX ""IX_TeamPlayer_NumberPerTeam"" ON ""TeamPlayer"" (""Number"", ""TeamId"");
+CREATE INDEX ""IX_TeamPlayer_Mentor_Id"" ON ""TeamPlayer"" (""Mentor_Id"");
+CREATE UNIQUE INDEX ""IX_Stadion_Main"" ON ""Stadions"" (""Street"", ""Name"");
+CREATE UNIQUE INDEX ""ReservedKeyWordTest"" ON ""Stadions"" (""Order"");
+CREATE INDEX ""IX_Stadion_Team_Id"" ON ""Stadions"" (""Team_Id"");
+CREATE INDEX ""IX_Foo_FooSelf1Id"" ON ""Foos"" (""FooSelf1Id"");
+CREATE INDEX ""IX_Foo_FooSelf2Id"" ON ""Foos"" (""FooSelf2Id"");
+CREATE INDEX ""IX_Foo_FooSelf3Id"" ON ""Foos"" (""FooSelf3Id"");
+CREATE INDEX ""IX_FooSelf_FooId"" ON ""FooSelves"" (""FooId"");
+CREATE INDEX ""IX_FooStep_FooId"" ON ""FooSteps"" (""FooId"");
+CREATE INDEX ""IX_FooRelationshipAFooCompositeKey_FooRelationshipA_Id"" ON ""FooRelationshipAFooCompositeKeys"" (""FooRelationshipA_Id"");
+CREATE INDEX ""IX_FooRelationshipAFooCompositeKey_FooCompositeKey_Id_FooCompositeKey_Version"" ON ""FooRelationshipAFooCompositeKeys"" (""FooCompositeKey_Id"", ""FooCompositeKey_Version"");
+CREATE INDEX ""IX_FooRelationshipBFooCompositeKey_FooRelationshipB_Id"" ON ""FooRelationshipBFooCompositeKeys"" (""FooRelationshipB_Id"");
+CREATE INDEX ""IX_FooRelationshipBFooCompositeKey_FooCompositeKey_Id_FooCompositeKey_Version"" ON ""FooRelationshipBFooCompositeKeys"" (""FooCompositeKey_Id"", ""FooCompositeKey_Version"");
+";
+
+ private static string generatedSql;
+
+ // Does not work on the build server. No clue why.
+
+ [TestMethod]
+ public void SqliteSqlGeneratorTest()
+ {
+ using (DbConnection connection = new SQLiteConnection("FullUri=file::memory:"))
+ {
+ // This is important! Else the in memory database will not work.
+ connection.Open();
+
+ using (var context = new DummyDbContext(connection))
+ {
+ // ReSharper disable once UnusedVariable
+ Player fo = context.Set().FirstOrDefault();
+
+ Assert.AreEqual(RemoveLineEndings(ReferenceSql), RemoveLineEndings(generatedSql));
+ }
+ }
+ }
+
+ private static string RemoveLineEndings(string input)
+ {
+ string lineSeparator = ((char)0x2028).ToString();
+ string paragraphSeparator = ((char)0x2029).ToString();
+ return input.Replace("\r\n", string.Empty).Replace("\n", string.Empty).Replace("\r", string.Empty).Replace(lineSeparator, string.Empty).Replace(paragraphSeparator, string.Empty);
+ }
+
+ private class DummyDbContext : DbContext
+ {
+ public DummyDbContext(DbConnection connection)
+ : base(connection, false)
+ {
+ }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ // This configuration contains all supported cases.
+ // So it makes a perfect test to validate whether the
+ // generated SQL is correct.
+ ModelConfiguration.Configure(modelBuilder);
+ var initializer = new AssertInitializer(modelBuilder);
+ Database.SetInitializer(initializer);
+ }
+ }
+
+ private class AssertInitializer : SqliteInitializerBase
+ {
+ public AssertInitializer(DbModelBuilder modelBuilder)
+ : base(modelBuilder)
+ {
+ }
+
+ public override void InitializeDatabase(DummyDbContext context)
+ {
+ DbModel model = ModelBuilder.Build(context.Database.Connection);
+ var sqliteSqlGenerator = new SqliteSqlGenerator();
+ generatedSql = sqliteSqlGenerator.Generate(model.StoreModel);
+ base.InitializeDatabase(context);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/Properties/AssemblyInfo.cs b/SQLite.CodeFirst.Test/Properties/AssemblyInfo.cs
deleted file mode 100644
index cda4f2d..0000000
--- a/SQLite.CodeFirst.Test/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System.Reflection;
-using System.Runtime.InteropServices;
-
-[assembly: AssemblyTitle("SQLite.CodeFirst.Test")]
-[assembly: AssemblyDescription("Contains the Unit Tests for the SQLite.CodeFirst Library.")]
-[assembly: AssemblyProduct("SQLite.CodeFirst")]
-[assembly: AssemblyCopyright("Copyright © Marc Sallin")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: AssemblyInformationalVersion("1.0.0.0")]
diff --git a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
index e4c30c6..a0798e2 100644
--- a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
+++ b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
@@ -1,124 +1,29 @@
-
-
+
- Debug
- AnyCPU
- {E542F38D-852E-489D-81C2-BF333503E10F}
- Library
- Properties
- SQLite.CodeFirst.Test
- SQLite.CodeFirst.Test
- v4.5.2
- 512
- {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- 10.0
- $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
- $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages
- False
- UnitTest
+ net48
+ SQLite.CodeFirst.Test
+ Contains the Unit Tests for the SQLite.CodeFirst Library.
+ 1.0.0.0
+ true
+ ..\Shared\SQLite.CodeFirst.StrongNameKey.snk
- true
full
- false
- bin\Debug\
- DEBUG;TRACE
- prompt
- 4
pdbonly
- true
- bin\Release\
- TRACE
- prompt
- 4
-
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
- True
-
-
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
- True
-
-
- ..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll
- True
- False
- ..\Lib\Moq.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
- False
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
- {50a32fe4-0e13-4213-a373-72523cdf34d9}
- SQLite.CodeFirst
-
+
-
+
+
-
-
-
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/IndexNameCreatorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/IndexNameCreatorTest.cs
new file mode 100644
index 0000000..1604fd7
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/IndexNameCreatorTest.cs
@@ -0,0 +1,23 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Builder.NameCreators;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Builder.NameCreators
+{
+ [TestClass]
+ public class IndexNameCreatorTest
+ {
+ [TestMethod]
+ public void CreateIndexName()
+ {
+ string result = IndexNameCreator.CreateName("MyTable", "MyProperty");
+ Assert.AreEqual("\"IX_MyTable_MyProperty\"", result);
+ }
+
+ [TestMethod]
+ public void CreateIndexNameEscaped()
+ {
+ string result = IndexNameCreator.CreateName("\"base.MyTable\"", "MyProperty");
+ Assert.AreEqual("\"IX_base.MyTable_MyProperty\"", result);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/NameCreatorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/NameCreatorTest.cs
new file mode 100644
index 0000000..1f28e45
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Builder/NameCreators/NameCreatorTest.cs
@@ -0,0 +1,16 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Builder.NameCreators;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Builder.NameCreators
+{
+ [TestClass]
+ public class NameCreatorTest
+ {
+ [TestMethod]
+ public void CreateTableNameTest()
+ {
+ string result = NameCreator.EscapeName("Test");
+ Assert.AreEqual("\"Test\"", result);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs
new file mode 100644
index 0000000..8697d24
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs
@@ -0,0 +1,27 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
+{
+ [TestClass]
+ public class CollateConstraintTest
+ {
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_NoConstraint()
+ {
+ var collationConstraint = new CollateConstraint();
+ collationConstraint.CollationFunction = CollationFunction.None;
+ string output = collationConstraint.CreateStatement();
+ Assert.AreEqual(output, "");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_NoCase()
+ {
+ var collationConstraint = new CollateConstraint();
+ collationConstraint.CollationFunction = CollationFunction.NoCase;
+ string output = collationConstraint.CreateStatement();
+ Assert.AreEqual(output, "COLLATE NOCASE");
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
similarity index 95%
rename from SQLite.CodeFirst.Test/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
index e8d9d22..eb6aa47 100644
--- a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
@@ -2,7 +2,7 @@
using Moq;
using SQLite.CodeFirst.Statement.ColumnConstraint;
-namespace SQLite.CodeFirst.Test.Statement.ColumnConstraint
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
{
[TestClass]
public class ColumnConstraintCollectionTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs
new file mode 100644
index 0000000..da2a20a
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs
@@ -0,0 +1,36 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
+{
+ [TestClass]
+ public class DefaultValueConstraintTest
+ {
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_IntDefault()
+ {
+ var defaultValueConstraint = new DefaultValueConstraint();
+ defaultValueConstraint.DefaultValue = "0";
+ string output = defaultValueConstraint.CreateStatement();
+ Assert.AreEqual(output, "DEFAULT (0)");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_StringDefault()
+ {
+ var defaultValueConstraint = new DefaultValueConstraint();
+ defaultValueConstraint.DefaultValue = @"'Something'";
+ string output = defaultValueConstraint.CreateStatement();
+ Assert.AreEqual(output, "DEFAULT ('Something')");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_ExpressionDefault()
+ {
+ var defaultValueConstraint = new DefaultValueConstraint();
+ defaultValueConstraint.DefaultValue = @"datetime('now')";
+ string output = defaultValueConstraint.CreateStatement();
+ Assert.AreEqual(output, "DEFAULT (datetime('now'))");
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/MaxLengthConstraint.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
similarity index 91%
rename from SQLite.CodeFirst.Test/Statement/ColumnConstraint/MaxLengthConstraint.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
index 34259b3..1df077f 100644
--- a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/MaxLengthConstraint.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
@@ -2,7 +2,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement.ColumnConstraint;
-namespace SQLite.CodeFirst.Test.Statement.ColumnConstraint
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
{
[TestClass]
public class MaxLengthConstraintTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/NotNullConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
similarity index 86%
rename from SQLite.CodeFirst.Test/Statement/ColumnConstraint/NotNullConstraintTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
index d2b3a91..747fdbf 100644
--- a/SQLite.CodeFirst.Test/Statement/ColumnConstraint/NotNullConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
@@ -1,7 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement.ColumnConstraint;
-namespace SQLite.CodeFirst.Test.Statement.ColumnConstraint
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
{
[TestClass]
public class NotNullConstraintTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs
new file mode 100644
index 0000000..5d5bc6d
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs
@@ -0,0 +1,25 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
+{
+ [TestClass]
+ public class PrimaryKeyConstraintTest
+ {
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_WithAutoincrement()
+ {
+ var primaryKeyConstraint = new PrimaryKeyConstraint { Autoincrement = true };
+ string output = primaryKeyConstraint.CreateStatement();
+ Assert.AreEqual(output, "PRIMARY KEY AUTOINCREMENT");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect()
+ {
+ var primaryKeyConstraint = new PrimaryKeyConstraint();
+ string output = primaryKeyConstraint.CreateStatement();
+ Assert.AreEqual(output, "PRIMARY KEY");
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs
new file mode 100644
index 0000000..d09e2c5
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs
@@ -0,0 +1,27 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Statement.ColumnConstraint
+{
+ [TestClass]
+ public class UniqueConstraintTest
+ {
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_NoConstraint()
+ {
+ var uniqueConstraint = new UniqueConstraint();
+ uniqueConstraint.OnConflict = OnConflictAction.None;
+ string output = uniqueConstraint.CreateStatement();
+ Assert.AreEqual(output, "UNIQUE");
+ }
+
+ [TestMethod]
+ public void CreateStatement_StatementIsCorrect_WithConstraint()
+ {
+ var uniqueConstraint = new UniqueConstraint();
+ uniqueConstraint.OnConflict = OnConflictAction.Rollback;
+ string output = uniqueConstraint.CreateStatement();
+ Assert.AreEqual(output, "UNIQUE ON CONFLICT ROLLBACK");
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnStatementCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
similarity index 95%
rename from SQLite.CodeFirst.Test/Statement/ColumnStatementCollectionTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
index fa0ef5c..eccb3fb 100644
--- a/SQLite.CodeFirst.Test/Statement/ColumnStatementCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
@@ -1,7 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class ColumnStatementCollectionTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/Statement/ColumnStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
similarity index 90%
rename from SQLite.CodeFirst.Test/Statement/ColumnStatementTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
index 3289303..9e47dc5 100644
--- a/SQLite.CodeFirst.Test/Statement/ColumnStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
@@ -3,7 +3,7 @@
using SQLite.CodeFirst.Statement;
using SQLite.CodeFirst.Statement.ColumnConstraint;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class ColumnStatementTest : StatementTestBase
@@ -16,9 +16,9 @@ public void CreateStatement()
var columnStatement = new ColumnStatement
{
- ColumnConstraints = columnConstraintsMock.Object,
ColumnName = "dummyColumnName",
- TypeName = "dummyType"
+ TypeName = "dummyType",
+ ColumnConstraints = columnConstraintsMock.Object,
};
string output = columnStatement.CreateStatement();
Assert.AreEqual(output, "[dummyColumnName] dummyType dummyColumnConstraint");
diff --git a/SQLite.CodeFirst.Test/Statement/CreateDatabaseStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
similarity index 95%
rename from SQLite.CodeFirst.Test/Statement/CreateDatabaseStatementTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
index d5f4c1e..33e4f39 100644
--- a/SQLite.CodeFirst.Test/Statement/CreateDatabaseStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
@@ -2,7 +2,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class CreateDatabaseStatementTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/Statement/CreateIndexStatementCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
similarity index 95%
rename from SQLite.CodeFirst.Test/Statement/CreateIndexStatementCollectionTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
index 41f2434..1082710 100644
--- a/SQLite.CodeFirst.Test/Statement/CreateIndexStatementCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
@@ -1,7 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class CreateIndexStatementCollectionTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/Statement/CreateTableStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
similarity index 92%
rename from SQLite.CodeFirst.Test/Statement/CreateTableStatementTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
index accf52a..31d3817 100644
--- a/SQLite.CodeFirst.Test/Statement/CreateTableStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
@@ -1,7 +1,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class CreateTableStatementTest : StatementTestBase
diff --git a/SQLite.CodeFirst.Test/Statement/ForeignKeyStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
similarity index 77%
rename from SQLite.CodeFirst.Test/Statement/ForeignKeyStatementTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
index 47943a1..c3472de 100644
--- a/SQLite.CodeFirst.Test/Statement/ForeignKeyStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
@@ -2,7 +2,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class ForeignKeyStatementTest : StatementTestBase
@@ -19,7 +19,7 @@ public void CreateStatementOneForeignKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1)");
+ Assert.AreEqual(output, "FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1])");
}
[TestMethod]
@@ -34,7 +34,7 @@ public void CreateStatementOneForeignKeyCascadeDeleteTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1) ON DELETE CASCADE");
+ Assert.AreEqual(output, "FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1]) ON DELETE CASCADE");
}
[TestMethod]
@@ -49,7 +49,7 @@ public void CreateStatementTwoForeignKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1, dummyForeignKey2) REFERENCES dummyForeignTable(dummForeignPrimaryKey1)");
+ Assert.AreEqual(output, "FOREIGN KEY ([dummyForeignKey1], [dummyForeignKey2]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1])");
}
[TestMethod]
@@ -64,7 +64,7 @@ public void CreateStatementTwoForeignKeyTwoPrimaryKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1, dummyForeignKey2) REFERENCES dummyForeignTable(dummForeignPrimaryKey1, dummForeignPrimaryKey2)");
+ Assert.AreEqual(output, "FOREIGN KEY ([dummyForeignKey1], [dummyForeignKey2]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1], [dummForeignPrimaryKey2])");
}
[TestMethod]
@@ -79,7 +79,7 @@ public void CreateStatementOneForeignKeyTwoPrimaryKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1, dummForeignPrimaryKey2)");
+ Assert.AreEqual(output, "FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1], [dummForeignPrimaryKey2])");
}
}
}
diff --git a/SQLite.CodeFirst.Test/Statement/PrimaryKeyStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
similarity index 69%
rename from SQLite.CodeFirst.Test/Statement/PrimaryKeyStatementTest.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
index 11204e3..beffd06 100644
--- a/SQLite.CodeFirst.Test/Statement/PrimaryKeyStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
@@ -2,7 +2,7 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
[TestClass]
public class PrimaryKeyStatementTest : StatementTestBase
@@ -12,9 +12,9 @@ public void CreateStatementWithOneKeyTest()
{
const string keyMember1 = "keyMember1";
- var primaryKeyStatement = new PrimaryKeyStatement(new List { keyMember1 });
+ var primaryKeyStatement = new CompositePrimaryKeyStatement(new List { keyMember1 });
Assert.AreEqual(primaryKeyStatement.Count, 1);
- Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY(keyMember1)");
+ Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY([keyMember1])");
}
[TestMethod]
@@ -23,9 +23,9 @@ public void CreateStatementWithTwoKeyTest()
const string keyMember1 = "keyMember1";
const string keyMember2 = "keyMember2";
- var primaryKeyStatement = new PrimaryKeyStatement(new List { keyMember1, keyMember2 });
+ var primaryKeyStatement = new CompositePrimaryKeyStatement(new List { keyMember1, keyMember2 });
Assert.AreEqual(primaryKeyStatement.Count, 2);
- Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY(keyMember1, keyMember2)");
+ Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY([keyMember1], [keyMember2])");
}
}
}
diff --git a/SQLite.CodeFirst.Test/Statement/StatementTestBase.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/StatementTestBase.cs
similarity index 93%
rename from SQLite.CodeFirst.Test/Statement/StatementTestBase.cs
rename to SQLite.CodeFirst.Test/UnitTests/Statement/StatementTestBase.cs
index d117e61..10250e3 100644
--- a/SQLite.CodeFirst.Test/Statement/StatementTestBase.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/StatementTestBase.cs
@@ -1,7 +1,7 @@
using Moq;
using SQLite.CodeFirst.Statement;
-namespace SQLite.CodeFirst.Test.Statement
+namespace SQLite.CodeFirst.Test.UnitTests.Statement
{
public abstract class StatementTestBase
{
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs
new file mode 100644
index 0000000..998f5c8
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs
@@ -0,0 +1,110 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Utility;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Utility
+{
+ [TestClass]
+ public class ConnectionStringParserTest
+ {
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource()
+ {
+ // Arrange
+ string connectionString = @"data source=.\db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@".\db\footballDb\footballDb.sqlite", result);
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_NotLowerCase()
+ {
+ // Arrange
+ string connectionString = @"DatA SOurce=.\db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@".\db\footballDb\footballDb.sqlite", result);
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_WithSpace()
+ {
+ // Arrange
+ string connectionString = @"DatA SOurce =.\db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@".\db\footballDb\footballDb.sqlite", result);
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_ReplacesDataDirectory()
+ {
+ // Arrange
+ string connectionString = @"data source=|DataDirectory|\db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.IsTrue(!result.StartsWith("|DataDirectory|"));
+ Assert.IsTrue(result.EndsWith(@"db\footballDb\footballDb.sqlite"));
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_ReplacesDataDirectoryCaseIsIgnored()
+ {
+ // Arrange
+ string connectionString = @"data source=|dAtadIrectory|\db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.IsTrue(!result.StartsWith("|dAtadIrectory|"));
+ Assert.IsTrue(result.EndsWith(@"\db\footballDb\footballDb.sqlite"));
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_ReplacesDataDirectoryAddsBackslash()
+ {
+ // Arrange
+ string connectionString = @"data source=|DataDirectory|db\footballDb\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.IsTrue(!result.StartsWith("|DataDirectory|"));
+ Assert.IsTrue(result.EndsWith(@"\db\footballDb\footballDb.sqlite"));
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_RemovesQuotationMark()
+ {
+ // Arrange
+ string connectionString = @"data source="".\db\footballDb\footballDb.sqlite"";foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@".\db\footballDb\footballDb.sqlite", result);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/HashCreatorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/HashCreatorTest.cs
new file mode 100644
index 0000000..6ce0367
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/HashCreatorTest.cs
@@ -0,0 +1,23 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Utility;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Utility
+{
+ [TestClass]
+ public class HashCreatorTest
+ {
+ [TestMethod]
+ public void CreateHashTest()
+ {
+ string result = HashCreator.CreateHash("Test,Test,Test");
+ Assert.AreEqual("kMbs8GbjyafvacMkuACV+tDtaoM9ii8y7pxi8AjgfcFincyIrDiD6R8kTiO5lupnmcYqZMUHtQk144aV3HTTCg==", result);
+ }
+
+ [TestMethod]
+ public void CreateHashNotSameHashTest()
+ {
+ string result = HashCreator.CreateHash("Test,Test,Test!");
+ Assert.AreNotEqual("kMbs8GbjyafvacMkuACV+tDtaoM9ii8y7pxi8AjgfcFincyIrDiD6R8kTiO5lupnmcYqZMUHtQk144aV3HTTCg==", result);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs
new file mode 100644
index 0000000..61fcfb2
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs
@@ -0,0 +1,57 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Utility;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Utility
+{
+ [TestClass]
+ public class HistoryEntityTypeValidatorTest
+ {
+ [TestMethod]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void EnsureValidTypeNotIHistory()
+ {
+ HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType1));
+ }
+
+ [TestMethod]
+ [ExpectedException(typeof(InvalidOperationException))]
+ public void EnsureValidTypeNoParamLessCtorTest()
+ {
+ HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType2));
+ }
+
+ [TestMethod]
+ public void EnsureValidTypeTest()
+ {
+ HistoryEntityTypeValidator.EnsureValidType(typeof(ValidFakeHistoryType));
+ }
+
+ private class ValidFakeHistoryType : IHistory
+ {
+ public int Id { get; set; }
+ public string Hash { get; set; }
+ public string Context { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+
+ private class InvalidFakeHistoryType1
+ {
+ public int Id { get; set; }
+ public string Hash { get; set; }
+ public string Context { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+
+ private class InvalidFakeHistoryType2 : IHistory
+ {
+ public InvalidFakeHistoryType2(string test)
+ { }
+
+ public int Id { get; set; }
+ public string Hash { get; set; }
+ public string Context { get; set; }
+ public DateTime CreateDate { get; set; }
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/app.config b/SQLite.CodeFirst.Test/app.config
new file mode 100644
index 0000000..2f401ce
--- /dev/null
+++ b/SQLite.CodeFirst.Test/app.config
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/packages.config b/SQLite.CodeFirst.Test/packages.config
deleted file mode 100644
index 1a41592..0000000
--- a/SQLite.CodeFirst.Test/packages.config
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst.sln b/SQLite.CodeFirst.sln
index 3a10ed8..856e8f8 100644
--- a/SQLite.CodeFirst.sln
+++ b/SQLite.CodeFirst.sln
@@ -1,80 +1,55 @@
-
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.23107.0
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29503.13
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.CodeFirst", "SQLite.CodeFirst\SQLite.CodeFirst.csproj", "{50A32FE4-0E13-4213-A373-72523CDF34D9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLite.CodeFirst", "SQLite.CodeFirst\SQLite.CodeFirst.csproj", "{50A32FE4-0E13-4213-A373-72523CDF34D9}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.CodeFirst.Console", "SQLite.CodeFirst.Console\SQLite.CodeFirst.Console.csproj", "{DEDABD86-6EA0-4673-A858-A4F71958F51D}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{01B97A84-5983-45C5-ACF8-168E56DF297C}"
- ProjectSection(SolutionItems) = preProject
- .nuget\NuGet.Config = .nuget\NuGet.Config
- .nuget\NuGet.exe = .nuget\NuGet.exe
- .nuget\NuGet.targets = .nuget\NuGet.targets
- EndProjectSection
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLite.CodeFirst.Console", "SQLite.CodeFirst.Console\SQLite.CodeFirst.Console.csproj", "{DEDABD86-6EA0-4673-A858-A4F71958F51D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{7031BD3C-AE76-43CD-91B6-B6BCD823968C}"
ProjectSection(SolutionItems) = preProject
- BuildAllConfigurations.proj = BuildAllConfigurations.proj
+ ci_appveyor.yml = ci_appveyor.yml
release_appveyor.yml = release_appveyor.yml
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.CodeFirst.Test", "SQLite.CodeFirst.Test\SQLite.CodeFirst.Test.csproj", "{E542F38D-852E-489D-81C2-BF333503E10F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLite.CodeFirst.Test", "SQLite.CodeFirst.Test\SQLite.CodeFirst.Test.csproj", "{E542F38D-852E-489D-81C2-BF333503E10F}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{436039AE-ADD6-48A5-81B9-C413E13344A9}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{1F6D4E0E-5D07-4889-961A-A6AFC0ECD7C7}"
ProjectSection(SolutionItems) = preProject
- Lib\Moq.dll = Lib\Moq.dll
+ README.md = README.md
+ Shared\SQLite.CodeFirst.ruleset = Shared\SQLite.CodeFirst.ruleset
+ Shared\SQLite.CodeFirst.StrongNameKey.snk = Shared\SQLite.CodeFirst.StrongNameKey.snk
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLite.CodeFirst.NetCore.Console", "SQLite.CodeFirst.NetCore.Console\SQLite.CodeFirst.NetCore.Console.csproj", "{709F46E3-D23B-4931-9985-8E09E3817CC4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
- Debug-40|Any CPU = Debug-40|Any CPU
- Debug-45|Any CPU = Debug-45|Any CPU
Release|Any CPU = Release|Any CPU
- Release-40|Any CPU = Release-40|Any CPU
- Release-45|Any CPU = Release-45|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug|Any CPU.ActiveCfg = Debug-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug|Any CPU.Build.0 = Debug-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug-40|Any CPU.ActiveCfg = Debug-40|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug-40|Any CPU.Build.0 = Debug-40|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug-45|Any CPU.ActiveCfg = Debug-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug-45|Any CPU.Build.0 = Debug-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release|Any CPU.ActiveCfg = Release-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release|Any CPU.Build.0 = Release-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release-40|Any CPU.ActiveCfg = Release-40|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release-40|Any CPU.Build.0 = Release-40|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release-45|Any CPU.ActiveCfg = Release-45|Any CPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release-45|Any CPU.Build.0 = Release-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug|Any CPU.ActiveCfg = Debug-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug|Any CPU.Build.0 = Debug-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug-40|Any CPU.ActiveCfg = Debug-40|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug-40|Any CPU.Build.0 = Debug-40|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug-45|Any CPU.ActiveCfg = Debug-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug-45|Any CPU.Build.0 = Debug-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release|Any CPU.ActiveCfg = Release-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release|Any CPU.Build.0 = Release-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release-40|Any CPU.ActiveCfg = Release-40|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release-40|Any CPU.Build.0 = Release-40|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release-45|Any CPU.ActiveCfg = Release-45|Any CPU
- {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release-45|Any CPU.Build.0 = Release-45|Any CPU
+ {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {50A32FE4-0E13-4213-A373-72523CDF34D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {50A32FE4-0E13-4213-A373-72523CDF34D9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DEDABD86-6EA0-4673-A858-A4F71958F51D}.Release|Any CPU.Build.0 = Release|Any CPU
{E542F38D-852E-489D-81C2-BF333503E10F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E542F38D-852E-489D-81C2-BF333503E10F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Debug-40|Any CPU.ActiveCfg = Debug|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Debug-40|Any CPU.Build.0 = Debug|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Debug-45|Any CPU.ActiveCfg = Debug|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Debug-45|Any CPU.Build.0 = Debug|Any CPU
{E542F38D-852E-489D-81C2-BF333503E10F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E542F38D-852E-489D-81C2-BF333503E10F}.Release|Any CPU.Build.0 = Release|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Release-40|Any CPU.ActiveCfg = Release|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Release-40|Any CPU.Build.0 = Release|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Release-45|Any CPU.ActiveCfg = Release|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.Release-45|Any CPU.Build.0 = Release|Any CPU
+ {709F46E3-D23B-4931-9985-8E09E3817CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {709F46E3-D23B-4931-9985-8E09E3817CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {709F46E3-D23B-4931-9985-8E09E3817CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {709F46E3-D23B-4931-9985-8E09E3817CC4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {D21CDFB4-5695-4346-AB51-48C80AED4418}
+ EndGlobalSection
EndGlobal
diff --git a/SQLite.CodeFirst/Builder/ColumnStatementCollectionBuilder.cs b/SQLite.CodeFirst/Builder/ColumnStatementCollectionBuilder.cs
deleted file mode 100644
index ca8d8b1..0000000
--- a/SQLite.CodeFirst/Builder/ColumnStatementCollectionBuilder.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using System.Collections.Generic;
-using System.Data.Entity.Core.Metadata.Edm;
-using System.Globalization;
-using System.Linq;
-using SQLite.CodeFirst.Statement;
-using SQLite.CodeFirst.Statement.ColumnConstraint;
-
-namespace SQLite.CodeFirst.Builder
-{
- internal class ColumnStatementCollectionBuilder : IStatementBuilder
- {
- private readonly IEnumerable properties;
-
- public ColumnStatementCollectionBuilder(IEnumerable properties)
- {
- this.properties = properties;
- }
-
- public ColumnStatementCollection BuildStatement()
- {
- var columnDefStatement = new ColumnStatementCollection(CreateColumnStatements().ToList());
- return columnDefStatement;
- }
-
- private IEnumerable CreateColumnStatements()
- {
- foreach (var property in properties)
- {
- var columnStatement = new ColumnStatement
- {
- ColumnName = property.Name,
- TypeName = property.TypeName,
- ColumnConstraints = new ColumnConstraintCollection()
- };
-
- AddMaxLengthConstraintIfNecessary(property, columnStatement);
- AdjustDatatypeForAutogenerationIfNecessary(property, columnStatement);
- AddNullConstraintIfNecessary(property, columnStatement);
-
- yield return columnStatement;
- }
- }
-
- private static void AddMaxLengthConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
- {
- if (property.MaxLength.HasValue)
- {
- columnStatement.ColumnConstraints.Add(new MaxLengthConstraint(property.MaxLength.Value));
- }
- }
-
- private static void AdjustDatatypeForAutogenerationIfNecessary(EdmProperty property, ColumnStatement columnStatement)
- {
- if (property.StoreGeneratedPattern == StoreGeneratedPattern.Identity)
- {
- // Must be INTEGER else SQLite will not generate the Ids
- columnStatement.TypeName = columnStatement.TypeName.ToLower(CultureInfo.InvariantCulture) == "int" ? "INTEGER" : columnStatement.TypeName;
- }
- }
-
- private static void AddNullConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
- {
- if (!property.Nullable && property.StoreGeneratedPattern != StoreGeneratedPattern.Identity)
- {
- // Only mark it as NotNull if it should not be generated.
- columnStatement.ColumnConstraints.Add(new NotNullConstraint());
- }
- }
- }
-}
diff --git a/SQLite.CodeFirst/Builder/CreateTableStatementBuilder.cs b/SQLite.CodeFirst/Builder/CreateTableStatementBuilder.cs
deleted file mode 100644
index 69081d4..0000000
--- a/SQLite.CodeFirst/Builder/CreateTableStatementBuilder.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data.Entity.Core.Metadata.Edm;
-using SQLite.CodeFirst.Builder.NameCreators;
-using SQLite.CodeFirst.Statement;
-using SQLite.CodeFirst.Utility;
-
-namespace SQLite.CodeFirst.Builder
-{
- internal class CreateTableStatementBuilder : IStatementBuilder
- {
- private readonly EntitySet entitySet;
- private readonly IEnumerable associationTypes;
-
- public CreateTableStatementBuilder(EntitySet entitySet, IEnumerable associationTypes)
- {
- this.entitySet = entitySet;
- this.associationTypes = associationTypes;
- }
-
- public CreateTableStatement BuildStatement()
- {
- var simpleColumnCollection = new ColumnStatementCollectionBuilder(entitySet.ElementType.Properties).BuildStatement();
- var primaryKeyStatement = new PrimaryKeyStatementBuilder(entitySet.ElementType.KeyMembers).BuildStatement();
- var foreignKeyCollection = new ForeignKeyStatementBuilder(associationTypes).BuildStatement();
-
- var columnStatements = new List();
- columnStatements.AddRange(simpleColumnCollection);
- columnStatements.Add(primaryKeyStatement);
- columnStatements.AddRange(foreignKeyCollection);
-
- return new CreateTableStatement
- {
- TableName = TableNameCreator.CreateTableName(entitySet.Table),
- ColumnStatementCollection = new ColumnStatementCollection(columnStatements)
- };
- }
- }
-}
diff --git a/SQLite.CodeFirst/Builder/NameCreators/TableNameCreator.cs b/SQLite.CodeFirst/Builder/NameCreators/TableNameCreator.cs
deleted file mode 100644
index 96cab32..0000000
--- a/SQLite.CodeFirst/Builder/NameCreators/TableNameCreator.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-using System.Globalization;
-
-namespace SQLite.CodeFirst.Builder.NameCreators
-{
- internal static class TableNameCreator
- {
- public static string CreateTableName(string tableFromEntitySet)
- {
- return String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", SpecialChars.EscapeCharOpen, tableFromEntitySet, SpecialChars.EscapeCharClose);
- }
- }
-}
diff --git a/SQLite.CodeFirst/Builder/PrimaryKeyStatementBuilder.cs b/SQLite.CodeFirst/Builder/PrimaryKeyStatementBuilder.cs
deleted file mode 100644
index f4f7d7c..0000000
--- a/SQLite.CodeFirst/Builder/PrimaryKeyStatementBuilder.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using System.Collections.Generic;
-using System.Data.Entity.Core.Metadata.Edm;
-using System.Linq;
-using SQLite.CodeFirst.Statement;
-
-namespace SQLite.CodeFirst.Builder
-{
- internal class PrimaryKeyStatementBuilder : IStatementBuilder
- {
- private readonly IEnumerable keyMembers;
-
- public PrimaryKeyStatementBuilder(IEnumerable keyMembers)
- {
- this.keyMembers = keyMembers;
- }
-
- public PrimaryKeyStatement BuildStatement()
- {
- return new PrimaryKeyStatement(keyMembers.Select(km => km.Name));
- }
- }
-}
diff --git a/SQLite.CodeFirst/Internal/AssemblyInfo.cs b/SQLite.CodeFirst/Internal/AssemblyInfo.cs
new file mode 100644
index 0000000..6de9d4c
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("SQLite.CodeFirst.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e35b9d3bf8a8ebcbbc7414b81cc803f1e43b24996e5af0d552c54743651fc328a50977e6e3c10b31c26165a74f3abbd9fdf2b785b8b9ba48568914aec8fea586a4ffa287c9dc5bd2b6367a3cb48e840baab417e89b27b95fa7ac5bc5b926cf9c45a76b064307845c71e9161bdb6eb7f96a6bf24ee6db96f7a4b71b7408938fae", AllInternalsVisible = true)]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
new file mode 100644
index 0000000..3fba1bb
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Entity.Core.Metadata.Edm;
+using System.Linq;
+using SQLite.CodeFirst.Extensions;
+using SQLite.CodeFirst.Statement;
+using SQLite.CodeFirst.Statement.ColumnConstraint;
+
+namespace SQLite.CodeFirst.Builder
+{
+ internal class ColumnStatementCollectionBuilder : IStatementBuilder
+ {
+ private readonly IEnumerable properties;
+ private readonly IEnumerable keyMembers;
+ private readonly Collation defaultCollation;
+
+ public ColumnStatementCollectionBuilder(IEnumerable properties, IEnumerable keyMembers, Collation defaultCollation)
+ {
+ this.properties = properties;
+ this.keyMembers = keyMembers;
+ this.defaultCollation = defaultCollation;
+ }
+
+ public ColumnStatementCollection BuildStatement()
+ {
+ var columnDefStatement = new ColumnStatementCollection(CreateColumnStatements().ToList());
+ return columnDefStatement;
+ }
+
+ private IEnumerable CreateColumnStatements()
+ {
+ foreach (var property in properties)
+ {
+ var columnStatement = new ColumnStatement
+ {
+ ColumnName = property.Name,
+ TypeName = property.TypeName,
+ ColumnConstraints = new ColumnConstraintCollection()
+ };
+
+ AddMaxLengthConstraintIfNecessary(property, columnStatement);
+ AdjustDatatypeForAutogenerationIfNecessary(property, columnStatement);
+ AddNullConstraintIfNecessary(property, columnStatement);
+ AddUniqueConstraintIfNecessary(property, columnStatement);
+ AddCollationConstraintIfNecessary(property, columnStatement, defaultCollation);
+ AddPrimaryKeyConstraintAndAdjustTypeIfNecessary(property, columnStatement);
+ AddDefaultValueConstraintIfNecessary(property, columnStatement);
+
+ yield return columnStatement;
+ }
+ }
+
+ private static void AddMaxLengthConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ if (property.MaxLength.HasValue)
+ {
+ columnStatement.ColumnConstraints.Add(new MaxLengthConstraint(property.MaxLength.Value));
+ }
+ }
+
+ private static void AdjustDatatypeForAutogenerationIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ if (property.StoreGeneratedPattern == StoreGeneratedPattern.Identity)
+ {
+ // Must be INTEGER else SQLite will not generate the Ids
+ ConvertIntegerType(columnStatement);
+ }
+ }
+
+ private static void AddNullConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ if (!property.Nullable && property.StoreGeneratedPattern != StoreGeneratedPattern.Identity)
+ {
+ // Only mark it as NotNull if it should not be generated.
+ columnStatement.ColumnConstraints.Add(new NotNullConstraint());
+ }
+ }
+
+ private static void AddCollationConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement, Collation defaultCollation)
+ {
+ var collateAttribute = property.GetCustomAnnotation();
+ if (property.PrimitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String)
+ {
+ // The column is a string type. Check if we have an explicit or default collation.
+ // If we have both, the explicitly chosen collation takes precedence.
+ var value = collateAttribute == null ? defaultCollation : collateAttribute.Collation;
+ if (value != null)
+ {
+ columnStatement.ColumnConstraints.Add(new CollateConstraint { CollationFunction = value.Function, CustomCollationFunction = value.CustomFunction });
+ }
+ }
+ else if (collateAttribute != null)
+ {
+ // Only string columns can be explicitly decorated with CollateAttribute.
+ var name = $"{property.DeclaringType.Name}.{property.Name}";
+ var errorMessage = $"CollateAttribute cannot be used on non-string property: {name} (underlying type is {property.PrimitiveType.PrimitiveTypeKind})";
+ throw new InvalidOperationException(errorMessage);
+ }
+ }
+
+ private static void AddUniqueConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ var value = property.GetCustomAnnotation();
+ if (value != null)
+ {
+ columnStatement.ColumnConstraints.Add(new UniqueConstraint { OnConflict = value.OnConflict });
+ }
+ }
+
+ private static void AddDefaultValueConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ var value = property.GetCustomAnnotation();
+ if (value != null)
+ {
+ columnStatement.ColumnConstraints.Add(new DefaultValueConstraint { DefaultValue = value.DefaultValue });
+ }
+ }
+
+ private void AddPrimaryKeyConstraintAndAdjustTypeIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ {
+ // Only handle a single primary key this way.
+ if (keyMembers.Count() != 1 || !property.Equals(keyMembers.Single()))
+ {
+ return;
+ }
+
+ ConvertIntegerType(columnStatement);
+ var primaryKeyConstraint = new PrimaryKeyConstraint();
+ primaryKeyConstraint.Autoincrement = property.GetCustomAnnotation() != null;
+ columnStatement.ColumnConstraints.Add(primaryKeyConstraint);
+ }
+
+ private static void ConvertIntegerType(ColumnStatement columnStatement)
+ {
+ const string integerType = "INTEGER";
+ columnStatement.TypeName = columnStatement.TypeName.ToUpperInvariant() == "INT" ? integerType : columnStatement.TypeName;
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Internal/Builder/CompositePrimaryKeyStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CompositePrimaryKeyStatementBuilder.cs
new file mode 100644
index 0000000..5550b05
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Builder/CompositePrimaryKeyStatementBuilder.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.Data.Entity.Core.Metadata.Edm;
+using System.Linq;
+using SQLite.CodeFirst.Statement;
+
+namespace SQLite.CodeFirst.Builder
+{
+ internal class CompositePrimaryKeyStatementBuilder : IStatementBuilder
+ {
+ private readonly IEnumerable keyMembers;
+
+ public CompositePrimaryKeyStatementBuilder(IEnumerable keyMembers)
+ {
+ this.keyMembers = keyMembers;
+ }
+
+ public CompositePrimaryKeyStatement BuildStatement()
+ {
+ return new CompositePrimaryKeyStatement(keyMembers.Select(km => km.Name));
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Builder/CreateDatabaseStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
similarity index 55%
rename from SQLite.CodeFirst/Builder/CreateDatabaseStatementBuilder.cs
rename to SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
index 2299e0a..ea87cca 100644
--- a/SQLite.CodeFirst/Builder/CreateDatabaseStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
-using SQLite.CodeFirst.Builder.NameCreators;
using SQLite.CodeFirst.Statement;
using SQLite.CodeFirst.Utility;
@@ -10,10 +9,12 @@ namespace SQLite.CodeFirst.Builder
internal class CreateDatabaseStatementBuilder : IStatementBuilder
{
private readonly EdmModel edmModel;
+ private readonly Collation defaultCollation;
- public CreateDatabaseStatementBuilder(EdmModel edmModel)
+ public CreateDatabaseStatementBuilder(EdmModel edmModel, Collation defaultCollation)
{
this.edmModel = edmModel;
+ this.defaultCollation = defaultCollation;
}
public CreateDatabaseStatement BuildStatement()
@@ -27,29 +28,11 @@ public CreateDatabaseStatement BuildStatement()
private IEnumerable GetCreateTableStatements()
{
+ var associationTypeContainer = new AssociationTypeContainer(edmModel.AssociationTypes, edmModel.Container);
+
foreach (var entitySet in edmModel.Container.EntitySets)
{
- ICollection associationTypes =
- edmModel.AssociationTypes.Where(a => a.Constraint.ToRole.Name == entitySet.Name).ToList();
-
- IList associationTypeWrappers = new List();
- foreach (var associationType in associationTypes)
- {
- string fromTable = edmModel.Container.GetEntitySetByName(associationType.Constraint.FromRole.Name, true).Table;
- string toTable = edmModel.Container.GetEntitySetByName(associationType.Constraint.ToRole.Name, true).Table;
-
- string fromTableName = TableNameCreator.CreateTableName(fromTable);
- string toTableName = TableNameCreator.CreateTableName(toTable);
-
- associationTypeWrappers.Add(new AssociationTypeWrapper
- {
- AssociationType = associationType,
- FromTableName = fromTableName,
- ToTableName = toTableName
- });
- }
-
- var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeWrappers);
+ var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeContainer, defaultCollation);
yield return tableStatementBuilder.BuildStatement();
}
}
diff --git a/SQLite.CodeFirst/Builder/CreateIndexStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateIndexStatementBuilder.cs
similarity index 87%
rename from SQLite.CodeFirst/Builder/CreateIndexStatementBuilder.cs
rename to SQLite.CodeFirst/Internal/Builder/CreateIndexStatementBuilder.cs
index b29a4b8..aaef01c 100644
--- a/SQLite.CodeFirst/Builder/CreateIndexStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/CreateIndexStatementBuilder.cs
@@ -1,10 +1,8 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure.Annotations;
-using System.Globalization;
using System.Linq;
using SQLite.CodeFirst.Builder.NameCreators;
using SQLite.CodeFirst.Extensions;
@@ -31,6 +29,7 @@ public CreateIndexStatementCollection BuildStatement()
.Select(x => x.Value)
.OfType();
+ string tableName = NameCreator.EscapeName(entitySet.Table);
foreach (var index in indexAnnotations.SelectMany(ia => ia.Indexes))
{
CreateIndexStatement createIndexStatement;
@@ -41,7 +40,7 @@ public CreateIndexStatementCollection BuildStatement()
{
IsUnique = index.IsUnique,
Name = indexName,
- Table = TableNameCreator.CreateTableName(entitySet.Table),
+ Table = tableName,
Columns = new Collection()
};
createIndexStatments.Add(indexName, createIndexStatement);
@@ -60,7 +59,7 @@ public CreateIndexStatementCollection BuildStatement()
private string GetIndexName(IndexAttribute index, EdmProperty property)
{
- return index.Name ?? IndexNameCreator.CreateIndexName(entitySet.ElementType.GetTableName(), property.Name);
+ return index.Name == null ? IndexNameCreator.CreateName(entitySet.ElementType.GetTableName(), property.Name) : NameCreator.EscapeName(index.Name);
}
}
}
diff --git a/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs
new file mode 100644
index 0000000..ac3198b
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.Data.Entity.Core.Metadata.Edm;
+using SQLite.CodeFirst.Builder.NameCreators;
+using SQLite.CodeFirst.Statement;
+using SQLite.CodeFirst.Utility;
+using SQLite.CodeFirst.Extensions;
+using System.Linq;
+
+namespace SQLite.CodeFirst.Builder
+{
+ internal class CreateTableStatementBuilder : IStatementBuilder
+ {
+ private readonly EntitySet entitySet;
+ private readonly AssociationTypeContainer associationTypeContainer;
+ private readonly Collation defaultCollation;
+
+ public CreateTableStatementBuilder(EntitySet entitySet, AssociationTypeContainer associationTypeContainer, Collation defaultCollation)
+ {
+ this.entitySet = entitySet;
+ this.associationTypeContainer = associationTypeContainer;
+ this.defaultCollation = defaultCollation;
+ }
+
+ public CreateTableStatement BuildStatement()
+ {
+ var keyMembers = entitySet.ElementType.KeyMembers.Cast().ToArray();
+
+ // Only create a CompositePrimaryKeyStatement if there is a composite primary key.
+ // If there is just one key member this is handled using a constraint.
+ CompositePrimaryKeyStatement compositePrimaryKeyStatement = null;
+ if (keyMembers.Length > 1)
+ {
+ compositePrimaryKeyStatement = new CompositePrimaryKeyStatementBuilder(keyMembers).BuildStatement();
+ }
+
+ var simpleColumnCollection = new ColumnStatementCollectionBuilder(entitySet.ElementType.Properties, keyMembers, defaultCollation).BuildStatement();
+ var foreignKeyCollection = new ForeignKeyStatementBuilder(associationTypeContainer.GetAssociationTypes(entitySet.Name)).BuildStatement();
+
+ var columnStatements = new List();
+ columnStatements.AddRange(simpleColumnCollection);
+ columnStatements.AddIfNotNull(compositePrimaryKeyStatement);
+ columnStatements.AddRange(foreignKeyCollection);
+
+ return new CreateTableStatement
+ {
+ TableName = NameCreator.EscapeName(entitySet.Table),
+ ColumnStatementCollection = new ColumnStatementCollection(columnStatements)
+ };
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Builder/ForeignKeyStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
similarity index 61%
rename from SQLite.CodeFirst/Builder/ForeignKeyStatementBuilder.cs
rename to SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
index 5e88a57..e5fd81a 100644
--- a/SQLite.CodeFirst/Builder/ForeignKeyStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
using SQLite.CodeFirst.Statement;
using SQLite.CodeFirst.Utility;
@@ -8,9 +7,9 @@ namespace SQLite.CodeFirst.Builder
{
internal class ForeignKeyStatementBuilder : IStatementBuilder
{
- private readonly IEnumerable associationTypes;
+ private readonly IEnumerable associationTypes;
- public ForeignKeyStatementBuilder(IEnumerable associationTypes)
+ public ForeignKeyStatementBuilder(IEnumerable associationTypes)
{
this.associationTypes = associationTypes;
}
@@ -27,10 +26,10 @@ private IEnumerable GetForeignKeyStatements()
{
yield return new ForeignKeyStatement
{
- ForeignKey = associationType.AssociationType.Constraint.ToProperties.Select(x => x.Name),
+ ForeignKey = associationType.ForeignKey,
ForeignTable = associationType.FromTableName,
- ForeignPrimaryKey = associationType.AssociationType.Constraint.FromProperties.Select(x => x.Name),
- CascadeDelete = associationType.AssociationType.Constraint.FromRole.DeleteBehavior == OperationAction.Cascade
+ ForeignPrimaryKey = associationType.ForeignPrimaryKey,
+ CascadeDelete = associationType.CascadeDelete
};
}
}
diff --git a/SQLite.CodeFirst/Builder/IStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/IStatementBuilder.cs
similarity index 74%
rename from SQLite.CodeFirst/Builder/IStatementBuilder.cs
rename to SQLite.CodeFirst/Internal/Builder/IStatementBuilder.cs
index 8d52cb9..8c37eda 100644
--- a/SQLite.CodeFirst/Builder/IStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/IStatementBuilder.cs
@@ -2,7 +2,7 @@
namespace SQLite.CodeFirst.Builder
{
- public interface IStatementBuilder
+ internal interface IStatementBuilder
where TStatement : IStatement
{
TStatement BuildStatement();
diff --git a/SQLite.CodeFirst/Internal/Builder/NameCreators/ColumnNameCreator.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/ColumnNameCreator.cs
new file mode 100644
index 0000000..c533dfd
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Builder/NameCreators/ColumnNameCreator.cs
@@ -0,0 +1,12 @@
+using System.Globalization;
+
+namespace SQLite.CodeFirst.Builder.NameCreators
+{
+ internal static class ColumnNameCreator
+ {
+ public static string EscapeName(string columnName)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "[{0}]", columnName);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Builder/NameCreators/IndexNameCreator.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/IndexNameCreator.cs
similarity index 89%
rename from SQLite.CodeFirst/Builder/NameCreators/IndexNameCreator.cs
rename to SQLite.CodeFirst/Internal/Builder/NameCreators/IndexNameCreator.cs
index 1098eb1..776de8b 100644
--- a/SQLite.CodeFirst/Builder/NameCreators/IndexNameCreator.cs
+++ b/SQLite.CodeFirst/Internal/Builder/NameCreators/IndexNameCreator.cs
@@ -7,7 +7,7 @@ internal static class IndexNameCreator
{
public const string IndexNamePrefix = "IX_";
- public static string CreateIndexName(string tableName, string propertyName)
+ public static string CreateName(string tableName, string propertyName)
{
// If the tableName is escaped it means that this name contains special chars e.g. a dot (base.myTable)
// Because the tablename is used to build the index name the index name must also be escaped.
@@ -15,4 +15,4 @@ public static string CreateIndexName(string tableName, string propertyName)
return String.Format(CultureInfo.InvariantCulture, "{0}{1}{2}_{3}{4}", SpecialChars.EscapeCharOpen, IndexNamePrefix, tableName, propertyName, SpecialChars.EscapeCharClose);
}
}
-}
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Internal/Builder/NameCreators/NameCreator.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/NameCreator.cs
new file mode 100644
index 0000000..56a8405
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Builder/NameCreators/NameCreator.cs
@@ -0,0 +1,19 @@
+using System.Globalization;
+
+namespace SQLite.CodeFirst.Builder.NameCreators
+{
+ internal static class NameCreator
+ {
+ public static string EscapeName(string name)
+ {
+ // Ensure that the name is not already escaped.
+ name = name.Trim(SpecialChars.EscapeCharOpen, SpecialChars.EscapeCharClose);
+
+ // Escape the escape chars, if there are some of them in the name.
+ name = name.Replace(SpecialChars.EscapeCharOpen.ToString(), SpecialChars.EscapeCharOpen.ToString() + SpecialChars.EscapeCharOpen);
+
+ // Escape the name.
+ return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", SpecialChars.EscapeCharOpen, name, SpecialChars.EscapeCharClose);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Builder/NameCreators/SpecialChars.cs b/SQLite.CodeFirst/Internal/Builder/NameCreators/SpecialChars.cs
similarity index 100%
rename from SQLite.CodeFirst/Builder/NameCreators/SpecialChars.cs
rename to SQLite.CodeFirst/Internal/Builder/NameCreators/SpecialChars.cs
diff --git a/SQLite.CodeFirst/Convention/SqliteForeignKeyIndexConvention.cs b/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
similarity index 87%
rename from SQLite.CodeFirst/Convention/SqliteForeignKeyIndexConvention.cs
rename to SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
index 6b6038a..e10205e 100644
--- a/SQLite.CodeFirst/Convention/SqliteForeignKeyIndexConvention.cs
+++ b/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
@@ -6,6 +6,7 @@
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration.Conventions;
+using System.Globalization;
using System.Linq;
using SQLite.CodeFirst.Builder.NameCreators;
@@ -17,7 +18,7 @@ namespace SQLite.CodeFirst.Convention
/// This is necessary because in SQLite an index-name must be unique.
/// Must be added right after the -Convention.
///
- public class SqliteForeignKeyIndexConvention : IStoreModelConvention
+ internal class SqliteForeignKeyIndexConvention : IStoreModelConvention
{
private const string IndexAnnotationName = "http://schemas.microsoft.com/ado/2013/11/edm/customannotation:Index";
@@ -45,7 +46,11 @@ public virtual void Apply(AssociationType item, DbModel model)
}
string tableName = item.Constraint.ToRole.GetEntityType().GetTableName();
- string propertyName = edmProperty.Name;
+
+ // Special treatment for composite primary keys
+ string propertyName = item.Constraint.FromProperties.Count > 1
+ ? String.Join("_", item.Constraint.ToProperties)
+ : edmProperty.Name;
// The original attribute is removed. The none-ForeignKeyIndicies will be remained and readded without any modification
// and the foreignKeyIncidies will be readded with the correct name.
@@ -69,14 +74,14 @@ public virtual void Apply(AssociationType item, DbModel model)
private static IndexAnnotation CreateIndexAnnotation(string tableName, string propertyName, int count)
{
- var indexName = IndexNameCreator.CreateIndexName(tableName, propertyName);
+ var indexName = IndexNameCreator.CreateName(tableName, propertyName);
// If there are two Indicies on the same property, the count is added.
// In SQLite an Index name must be global unique.
// To be honest, it should never happen. But because its possible by using the API, it should be covered.
if (count > 0)
{
- indexName = String.Format("{0}_{1}", indexName, count);
+ indexName = String.Format(CultureInfo.InvariantCulture, "{0}_{1}", indexName, count);
}
var indexAttribute = new IndexAttribute(indexName);
diff --git a/SQLite.CodeFirst/Internal/Extensions/DbModelBuilderExtensions.cs b/SQLite.CodeFirst/Internal/Extensions/DbModelBuilderExtensions.cs
new file mode 100644
index 0000000..bf12c83
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Extensions/DbModelBuilderExtensions.cs
@@ -0,0 +1,16 @@
+using System.Data.Entity;
+using System.Linq;
+
+namespace SQLite.CodeFirst.Extensions
+{
+ internal static class DbModelBuilderExtensions
+ {
+ public static void RegisterAttributeAsColumnAnnotation(this DbModelBuilder modelBuilder)
+ where TAttribute : class
+ {
+ modelBuilder.Properties()
+ .Having(x => x.GetCustomAttributes(false).OfType().FirstOrDefault())
+ .Configure((config, attribute) => config.HasColumnAnnotation(typeof(TAttribute).Name, attribute));
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Internal/Extensions/EdmPropertyExtensions.cs b/SQLite.CodeFirst/Internal/Extensions/EdmPropertyExtensions.cs
new file mode 100644
index 0000000..1fb88a8
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Extensions/EdmPropertyExtensions.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Data.Entity.Core.Metadata.Edm;
+
+namespace SQLite.CodeFirst.Extensions
+{
+ internal static class EdmPropertyExtensions
+ {
+ public static TAttribute GetCustomAnnotation(this EdmProperty property)
+ where TAttribute : Attribute
+ {
+ MetadataProperty item;
+ bool found = property.MetadataProperties.TryGetValue("http://schemas.microsoft.com/ado/2013/11/edm/customannotation:" + typeof(TAttribute).Name, true, out item);
+ if (found)
+ {
+ return (TAttribute) item.Value;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Extensions/EntityTypeExtension.cs b/SQLite.CodeFirst/Internal/Extensions/EntityTypeExtension.cs
similarity index 94%
rename from SQLite.CodeFirst/Extensions/EntityTypeExtension.cs
rename to SQLite.CodeFirst/Internal/Extensions/EntityTypeExtension.cs
index f2726c1..95282f4 100644
--- a/SQLite.CodeFirst/Extensions/EntityTypeExtension.cs
+++ b/SQLite.CodeFirst/Internal/Extensions/EntityTypeExtension.cs
@@ -26,7 +26,7 @@ public static string GetTableName(this EntityType entityType)
// GetValue() overload with one value was introduces in .net 4.5 so use the overload with two parameters.
var name = (string)metadataPropertyValueType.GetProperty("Name").GetValue(metadataPropertyValue, null);
- return TableNameCreator.CreateTableName(name);
+ return NameCreator.EscapeName(name);
}
}
}
diff --git a/SQLite.CodeFirst/Internal/Extensions/ListExtensions.cs b/SQLite.CodeFirst/Internal/Extensions/ListExtensions.cs
new file mode 100644
index 0000000..51b5bf4
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Extensions/ListExtensions.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace SQLite.CodeFirst.Extensions
+{
+ public static class ListExtensions
+ {
+ public static void AddIfNotNull(this ICollection list, T element)
+ {
+ if (list == null || element == null)
+ {
+ return;
+ }
+
+ list.Add(element);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/CollateConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/CollateConstraint.cs
new file mode 100644
index 0000000..74068be
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/CollateConstraint.cs
@@ -0,0 +1,28 @@
+using System.Text;
+
+namespace SQLite.CodeFirst.Statement.ColumnConstraint
+{
+ internal class CollateConstraint : IColumnConstraint
+ {
+ private const string Template = "COLLATE {collation-name}";
+
+ public CollationFunction CollationFunction { get; set; }
+
+ public string CustomCollationFunction { get; set; }
+
+ public string CreateStatement()
+ {
+ if (CollationFunction == CollationFunction.None)
+ {
+ return string.Empty;
+ }
+
+ var sb = new StringBuilder(Template);
+
+ string name = CollationFunction == CollationFunction.Custom ? CustomCollationFunction : CollationFunction.ToString().ToUpperInvariant();
+ sb.Replace("{collation-name}", name);
+
+ return sb.ToString().Trim();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/ColumnConstraintCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/ColumnConstraintCollection.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnConstraint/ColumnConstraintCollection.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/ColumnConstraintCollection.cs
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/DefaultValueConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/DefaultValueConstraint.cs
new file mode 100644
index 0000000..9c41c22
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/DefaultValueConstraint.cs
@@ -0,0 +1,20 @@
+using System.Text;
+
+namespace SQLite.CodeFirst.Statement.ColumnConstraint
+{
+ internal class DefaultValueConstraint : IColumnConstraint
+ {
+ private const string Template = "DEFAULT ({defaultValue})";
+
+ public string DefaultValue { get; set; }
+
+ public string CreateStatement()
+ {
+ var sb = new StringBuilder(Template);
+
+ sb.Replace("{defaultValue}", DefaultValue);
+
+ return sb.ToString().Trim();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraint.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraint.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraint.cs
diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraintCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraintCollection.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnConstraint/IColumnConstraintCollection.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/IColumnConstraintCollection.cs
diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/MaxLengthConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/MaxLengthConstraint.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnConstraint/MaxLengthConstraint.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/MaxLengthConstraint.cs
diff --git a/SQLite.CodeFirst/Statement/ColumnConstraint/NotNullConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/NotNullConstraint.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnConstraint/NotNullConstraint.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnConstraint/NotNullConstraint.cs
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/PrimaryKeyConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/PrimaryKeyConstraint.cs
new file mode 100644
index 0000000..c3c94f9
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/PrimaryKeyConstraint.cs
@@ -0,0 +1,20 @@
+using System.Text;
+
+namespace SQLite.CodeFirst.Statement.ColumnConstraint
+{
+ internal class PrimaryKeyConstraint : IColumnConstraint
+ {
+ private const string Template = "PRIMARY KEY {autoincrement}";
+
+ public bool Autoincrement { get; set; }
+
+ public string CreateStatement()
+ {
+ var sb = new StringBuilder(Template);
+
+ sb.Replace("{autoincrement}", Autoincrement ? "AUTOINCREMENT" : string.Empty);
+
+ return sb.ToString().Trim();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs
new file mode 100644
index 0000000..7859686
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnConstraint/UniqueConstraint.cs
@@ -0,0 +1,20 @@
+using System.Text;
+
+namespace SQLite.CodeFirst.Statement.ColumnConstraint
+{
+ internal class UniqueConstraint : IColumnConstraint
+ {
+ private const string Template = "UNIQUE {conflict-clause}";
+
+ public OnConflictAction OnConflict { get; set; }
+
+ public string CreateStatement()
+ {
+ var sb = new StringBuilder(Template);
+
+ sb.Replace("{conflict-clause}", OnConflict != OnConflictAction.None ? "ON CONFLICT " + OnConflict.ToString().ToUpperInvariant() : string.Empty);
+
+ return sb.ToString().Trim();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Statement/ColumnStatement.cs b/SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs
similarity index 74%
rename from SQLite.CodeFirst/Statement/ColumnStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs
index bf4f91d..d55b091 100644
--- a/SQLite.CodeFirst/Statement/ColumnStatement.cs
+++ b/SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs
@@ -1,11 +1,12 @@
using System.Text;
+using SQLite.CodeFirst.Builder.NameCreators;
using SQLite.CodeFirst.Statement.ColumnConstraint;
namespace SQLite.CodeFirst.Statement
{
internal class ColumnStatement : IStatement
{
- private const string Template = "[{column-name}] {type-name} {column-constraint}";
+ private const string Template = "{column-name} {type-name} {column-constraint}";
public string ColumnName { get; set; }
public string TypeName { get; set; }
@@ -15,7 +16,7 @@ public string CreateStatement()
{
var sb = new StringBuilder(Template);
- sb.Replace("{column-name}", ColumnName);
+ sb.Replace("{column-name}", ColumnNameCreator.EscapeName(ColumnName));
sb.Replace("{type-name}", TypeName);
sb.Replace("{column-constraint}", ColumnConstraints.CreateStatement());
diff --git a/SQLite.CodeFirst/Statement/ColumnStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/ColumnStatementCollection.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/ColumnStatementCollection.cs
rename to SQLite.CodeFirst/Internal/Statement/ColumnStatementCollection.cs
diff --git a/SQLite.CodeFirst/Statement/PrimaryKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
similarity index 57%
rename from SQLite.CodeFirst/Statement/PrimaryKeyStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
index ff25587..196f305 100644
--- a/SQLite.CodeFirst/Statement/PrimaryKeyStatement.cs
+++ b/SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
@@ -1,15 +1,16 @@
-using System;
+using SQLite.CodeFirst.Builder.NameCreators;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Linq;
namespace SQLite.CodeFirst.Statement
{
- internal class PrimaryKeyStatement : Collection, IStatement
+ internal class CompositePrimaryKeyStatement : Collection, IStatement
{
private const string Template = "PRIMARY KEY({primary-keys})";
- private const string PrimaryKeyColumnNameSeperator = ", ";
- public PrimaryKeyStatement(IEnumerable keyMembers)
+ public CompositePrimaryKeyStatement(IEnumerable keyMembers)
{
foreach (var keyMember in keyMembers)
{
@@ -19,7 +20,7 @@ public PrimaryKeyStatement(IEnumerable keyMembers)
public string CreateStatement()
{
- string primaryKeys = String.Join(PrimaryKeyColumnNameSeperator, this);
+ string primaryKeys = String.Join(", ", this.Select(c => ColumnNameCreator.EscapeName(c)));
return Template.Replace("{primary-keys}", primaryKeys);
}
}
diff --git a/SQLite.CodeFirst/Statement/CreateDatabaseStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateDatabaseStatement.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/CreateDatabaseStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/CreateDatabaseStatement.cs
diff --git a/SQLite.CodeFirst/Statement/CreateIndexStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateIndexStatement.cs
similarity index 91%
rename from SQLite.CodeFirst/Statement/CreateIndexStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/CreateIndexStatement.cs
index b153ad1..8eb4e63 100644
--- a/SQLite.CodeFirst/Statement/CreateIndexStatement.cs
+++ b/SQLite.CodeFirst/Internal/Statement/CreateIndexStatement.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using SQLite.CodeFirst.Builder.NameCreators;
namespace SQLite.CodeFirst.Statement
{
@@ -23,7 +24,7 @@ public string CreateStatement()
stringBuilder.Replace("{index-name}", Name);
stringBuilder.Replace("{table-name}", Table);
- IEnumerable orderedColumnNames = Columns.OrderBy(c => c.Order).Select(c => c.Name);
+ IEnumerable orderedColumnNames = Columns.OrderBy(c => c.Order).Select(c => c.Name).Select(NameCreator.EscapeName);
string columnDefinition = String.Join(ColumnNameSeperator, orderedColumnNames);
stringBuilder.Replace("{column-def}", columnDefinition);
diff --git a/SQLite.CodeFirst/Statement/CreateIndexStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/CreateIndexStatementCollection.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/CreateIndexStatementCollection.cs
rename to SQLite.CodeFirst/Internal/Statement/CreateIndexStatementCollection.cs
diff --git a/SQLite.CodeFirst/Statement/CreateTableStatement.cs b/SQLite.CodeFirst/Internal/Statement/CreateTableStatement.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/CreateTableStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/CreateTableStatement.cs
diff --git a/SQLite.CodeFirst/Statement/ForeignKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs
similarity index 82%
rename from SQLite.CodeFirst/Statement/ForeignKeyStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs
index 19b61b4..58b4974 100644
--- a/SQLite.CodeFirst/Statement/ForeignKeyStatement.cs
+++ b/SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using SQLite.CodeFirst.Builder.NameCreators;
+using System.Collections.Generic;
+using System.Linq;
using System.Text;
namespace SQLite.CodeFirst.Statement
@@ -16,11 +18,9 @@ internal class ForeignKeyStatement : IStatement
public string CreateStatement()
{
var sb = new StringBuilder(Template);
-
- sb.Replace("{foreign-key}", string.Join(", ", ForeignKey));
+ sb.Replace("{foreign-key}", string.Join(", ", ForeignKey.Select(c => ColumnNameCreator.EscapeName(c))));
sb.Replace("{referenced-table}", ForeignTable);
- sb.Replace("{referenced-id}", string.Join(", ", ForeignPrimaryKey));
-
+ sb.Replace("{referenced-id}", string.Join(", ", ForeignPrimaryKey.Select(c => ColumnNameCreator.EscapeName(c))));
if (CascadeDelete)
{
sb.Append(" " + CascadeDeleteStatement);
diff --git a/SQLite.CodeFirst/Statement/IStatement.cs b/SQLite.CodeFirst/Internal/Statement/IStatement.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/IStatement.cs
rename to SQLite.CodeFirst/Internal/Statement/IStatement.cs
diff --git a/SQLite.CodeFirst/Statement/IStatementCollection.cs b/SQLite.CodeFirst/Internal/Statement/IStatementCollection.cs
similarity index 100%
rename from SQLite.CodeFirst/Statement/IStatementCollection.cs
rename to SQLite.CodeFirst/Internal/Statement/IStatementCollection.cs
diff --git a/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs b/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs
new file mode 100644
index 0000000..e86d20f
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs
@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+using System.Data.Entity.Core.Metadata.Edm;
+using System.Linq;
+
+namespace SQLite.CodeFirst.Utility
+{
+ internal class AssociationTypeContainer
+ {
+ private readonly IEnumerable sqliteAssociationTypes;
+
+ public AssociationTypeContainer(IEnumerable associationTypes, EntityContainer container)
+ {
+ sqliteAssociationTypes = associationTypes.Select(associationType => new SqliteAssociationType(associationType, container));
+ }
+
+ public IEnumerable GetAssociationTypes(string entitySetName)
+ {
+ return sqliteAssociationTypes.Where(associationType => associationType.ToRoleEntitySetName == entitySetName);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/SqliteConnectionStringParser.cs b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
similarity index 66%
rename from SQLite.CodeFirst/SqliteConnectionStringParser.cs
rename to SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
index 8d7d12d..02c2c21 100644
--- a/SQLite.CodeFirst/SqliteConnectionStringParser.cs
+++ b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
@@ -1,18 +1,40 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
+using System.IO;
-namespace SQLite.CodeFirst
+namespace SQLite.CodeFirst.Utility
{
- internal static class SqliteConnectionStringParser
+ internal static class ConnectionStringParser
{
private const string DataDirectoryToken = "|datadirectory|";
+ private const string DataSourceToken = "data source";
private const char KeyValuePairSeperator = ';';
private const char KeyValueSeperator = '=';
private const int KeyPosition = 0;
private const int ValuePosition = 1;
- public static IDictionary ParseSqliteConnectionString(string connectionString)
+ public static string GetDataSource(string connectionString)
+ {
+ // If the datasource token does not exists this is a FullUri connection string.
+ IDictionary strings = ParseConnectionString(connectionString);
+ if (strings.ContainsKey(DataSourceToken))
+ {
+ var path = ExpandDataDirectory(ParseConnectionString(connectionString)[DataSourceToken]);
+ return path.Trim('"');
+ }
+
+ // TODO: Implement FullUri parsing.
+ if (connectionString.Contains(":memory:"))
+ {
+ return ":memory:";
+ }
+ throw new NotSupportedException("FullUri format is currently only supported for :memory:.");
+ }
+
+ [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "ToUppercase makes no sense.")]
+ private static IDictionary ParseConnectionString(string connectionString)
{
connectionString = connectionString.Trim();
string[] keyValuePairs = connectionString.Split(KeyValuePairSeperator);
@@ -23,21 +45,13 @@ public static IDictionary ParseSqliteConnectionString(string con
string[] keyValue = keyValuePair.Split(KeyValueSeperator);
if (keyValue.Length >= 2)
{
- keyValuePairDictionary.Add(keyValue[KeyPosition].ToLower(CultureInfo.InvariantCulture), keyValue[ValuePosition]);
+ keyValuePairDictionary.Add(keyValue[KeyPosition].Trim().ToLower(CultureInfo.InvariantCulture), keyValue[ValuePosition]);
}
}
return keyValuePairDictionary;
}
- public static string GetDataSource(string connectionString)
- {
- var path = ExpandDataDirectory(ParseSqliteConnectionString(connectionString)["data source"]);
- // remove quotation mark if exists
- path = path.Trim('"');
- return path;
- }
-
private static string ExpandDataDirectory(string path)
{
if (path == null || !path.StartsWith(DataDirectoryToken, StringComparison.OrdinalIgnoreCase))
@@ -61,14 +75,14 @@ private static string ExpandDataDirectory(string path)
// We don't know if rootFolderpath ends with '\', and we don't know if the given name starts with onw
int fileNamePosition = DataDirectoryToken.Length; // filename starts right after the '|datadirectory|' keyword
- bool rootFolderEndsWith = (0 < rootFolderPath.Length) && rootFolderPath[rootFolderPath.Length - 1] == '\\';
- bool fileNameStartsWith = (fileNamePosition < path.Length) && path[fileNamePosition] == '\\';
+ bool rootFolderEndsWith = (0 < rootFolderPath.Length) && rootFolderPath[rootFolderPath.Length - 1] == Path.DirectorySeparatorChar;
+ bool fileNameStartsWith = (fileNamePosition < path.Length) && path[fileNamePosition] == Path.DirectorySeparatorChar;
// replace |datadirectory| with root folder path
if (!rootFolderEndsWith && !fileNameStartsWith)
{
// need to insert '\'
- fullPath = rootFolderPath + '\\' + path.Substring(fileNamePosition);
+ fullPath = rootFolderPath + Path.DirectorySeparatorChar + path.Substring(fileNamePosition);
}
else if (rootFolderEndsWith && fileNameStartsWith)
{
diff --git a/SQLite.CodeFirst/Internal/Utility/HashCreator.cs b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs
new file mode 100644
index 0000000..f8732a6
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace SQLite.CodeFirst.Utility
+{
+ internal static class HashCreator
+ {
+ public static string CreateHash(string data)
+ {
+ byte[] dataBytes = Encoding.ASCII.GetBytes(data);
+ using (SHA512 sha512 = SHA512.Create())
+ {
+ byte[] hashBytes = sha512.ComputeHash(dataBytes);
+ string hash = Convert.ToBase64String(hashBytes);
+ return hash;
+ }
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs b/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs
new file mode 100644
index 0000000..459e2ee
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/HistoryEntityTypeValidator.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace SQLite.CodeFirst.Utility
+{
+ internal class HistoryEntityTypeValidator
+ {
+ public static void EnsureValidType(Type historyEntityType)
+ {
+ if (!typeof(IHistory).IsAssignableFrom(historyEntityType))
+ {
+ throw new InvalidOperationException("The Type " + historyEntityType.Name + " does not implement the IHistory interface.");
+ }
+ if (historyEntityType.GetConstructor(Type.EmptyTypes) == null)
+ {
+ throw new InvalidOperationException("The Type " + historyEntityType.Name + " does not provide an parameterless constructor.");
+ }
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Internal/Utility/InMemoryAwareFile.cs b/SQLite.CodeFirst/Internal/Utility/InMemoryAwareFile.cs
new file mode 100644
index 0000000..d04c21c
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/InMemoryAwareFile.cs
@@ -0,0 +1,76 @@
+using System;
+using System.IO;
+
+namespace SQLite.CodeFirst.Utility
+{
+ internal class InMemoryAwareFile
+ {
+ public static FileAttributes? GetFileAttributes(string path)
+ {
+ if (IsInMemoryPath(path))
+ {
+ return null;
+ }
+ return File.GetAttributes(path);
+ }
+
+ public static void SetFileAttributes(string path, FileAttributes? fileAttributes)
+ {
+ if (IsInMemoryPath(path) || fileAttributes == null)
+ {
+ return;
+ }
+ File.SetAttributes(path, fileAttributes.Value);
+ }
+
+ public static bool Exists(string path)
+ {
+ if (IsInMemoryPath(path))
+ {
+ return false;
+ }
+ return File.Exists(path);
+ }
+
+ public static bool Exists(string path, bool nullByteFileMeansNotExisting)
+ {
+ if (IsInMemoryPath(path))
+ {
+ return false;
+ }
+
+ var fileInfo = new FileInfo(path);
+ return nullByteFileMeansNotExisting ? fileInfo.Exists && fileInfo.Length != 0 : fileInfo.Exists;
+ }
+
+ public static void Delete(string path)
+ {
+ if (IsInMemoryPath(path))
+ {
+ return;
+ }
+
+ File.Delete(path);
+ }
+
+ public static void CreateDirectory(string path)
+ {
+ if (IsInMemoryPath(path))
+ {
+ return;
+ }
+
+ var dbFileInfo = new FileInfo(path);
+ if (dbFileInfo.Directory != null)
+ {
+ dbFileInfo.Directory.Create();
+ }
+ }
+
+
+ private static bool IsInMemoryPath(string path)
+ {
+ return string.Equals(path, ":memory:", StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs b/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs
new file mode 100644
index 0000000..6ab4f49
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs
@@ -0,0 +1,51 @@
+using System.Collections.Generic;
+using System.Data.Entity.Core.Metadata.Edm;
+using System.Linq;
+using SQLite.CodeFirst.Builder.NameCreators;
+
+namespace SQLite.CodeFirst.Utility
+{
+ internal class SqliteAssociationType
+ {
+ public SqliteAssociationType(AssociationType associationType, EntityContainer container)
+ {
+ FromRoleEntitySetName = associationType.Constraint.FromRole.Name;
+ ToRoleEntitySetName = associationType.Constraint.ToRole.Name;
+
+ string fromTable = container.GetEntitySetByName(FromRoleEntitySetName, true).Table;
+ string toTable;
+
+ if (IsSelfReferencing(associationType))
+ {
+ toTable = fromTable;
+ ToRoleEntitySetName = FromRoleEntitySetName;
+ }
+ else
+ {
+ toTable = container.GetEntitySetByName(ToRoleEntitySetName, true).Table;
+ }
+
+ FromTableName = NameCreator.EscapeName(fromTable);
+ ToTableName = NameCreator.EscapeName(toTable);
+ ForeignKey = associationType.Constraint.ToProperties.Select(x => x.Name);
+ ForeignPrimaryKey = associationType.Constraint.FromProperties.Select(x => x.Name);
+ CascadeDelete = associationType.Constraint.FromRole.DeleteBehavior == OperationAction.Cascade;
+ }
+
+ private static bool IsSelfReferencing(AssociationType associationType)
+ {
+ var toRoleRefType = (RefType)associationType.Constraint.ToRole.TypeUsage.EdmType;
+ var fromRoleRefType = (RefType)associationType.Constraint.FromRole.TypeUsage.EdmType;
+ bool isSelfReferencing = toRoleRefType.ElementType.Name == fromRoleRefType.ElementType.Name;
+ return isSelfReferencing;
+ }
+
+ public string ToRoleEntitySetName { get; set; }
+ public string FromRoleEntitySetName { get; set; }
+ public IEnumerable ForeignKey { get; }
+ public string FromTableName { get; }
+ public string ToTableName { get; }
+ public IEnumerable ForeignPrimaryKey { get; }
+ public bool CascadeDelete { get; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Properties/AssemblyInfo.cs b/SQLite.CodeFirst/Properties/AssemblyInfo.cs
deleted file mode 100644
index cb88143..0000000
--- a/SQLite.CodeFirst/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-[assembly: AssemblyTitle("SQLite.CodeFirst")]
-[assembly: AssemblyDescription(
- "Creates a SQLite Database from Code, using Entity Framework CodeFirst. " +
- "This Project ships several IDbInitializer which creates " +
- "a new SQLite Database, based on your model/code.")]
-[assembly: AssemblyProduct("SQLite.CodeFirst")]
-[assembly: AssemblyCopyright("Copyright © Marc Sallin")]
-[assembly: AssemblyCompany("Marc Sallin")]
-
-[assembly: InternalsVisibleTo("SQLite.CodeFirst.Test", AllInternalsVisible = true)]
-[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
-
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
-// COM, set the ComVisible attribute to true on that type.
-[assembly: ComVisible(false)]
-
-// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("46603902-448a-4c50-87ec-09cb792b740f")]
-
-// Will be replaced by the build server
-[assembly: AssemblyVersion("1.0.0.0")]
-[assembly: AssemblyFileVersion("1.0.0.0")]
-[assembly: AssemblyInformationalVersion("1.0.0.0")]
diff --git a/SQLite.CodeFirst/Public/Attributes/AutoincrementAttribute.cs b/SQLite.CodeFirst/Public/Attributes/AutoincrementAttribute.cs
new file mode 100644
index 0000000..4dde7db
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/AutoincrementAttribute.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Decorate an primary key with this attribute to create a "INTEGER PRIMARY KEY AUTOINCREMENT" column.
+ ///
+ /// 1. The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not
+ /// strictly needed. It is usually not needed.
+ /// 2. In SQLite, a column with type INTEGER PRIMARY KEY is an alias for the ROWID(except in WITHOUT ROWID tables) which
+ /// is always a 64-bit signed integer.
+ /// 3. On an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled
+ /// automatically with an unused integer, usually one more than the largest ROWID currently in use.This is true
+ /// regardless of whether or not the AUTOINCREMENT keyword is used.
+ /// 4. If the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that changes the automatic ROWID assignment
+ /// algorithm to prevent the reuse of ROWIDs over the lifetime of the database.In other words, the purpose of
+ /// AUTOINCREMENT is to prevent the reuse of ROWIDs from previously deleted rows.
+ /// http://sqlite.org/autoinc.html [24.02.2017]
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class AutoincrementAttribute : Attribute
+ {}
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs b/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs
new file mode 100644
index 0000000..636965a
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// When SQLite compares two strings, it uses a collating sequence or collating function (two words for the same thing)
+ /// to determine which string is greater or if the two strings are equal. SQLite has three built-in collating functions (see ).
+ /// It is possible to specify a custom collating function. Set to and specify the name using the function parameter.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class CollateAttribute : Attribute
+ {
+ public CollateAttribute()
+ {
+ Collation = new Collation();
+ }
+
+ public CollateAttribute(CollationFunction function)
+ {
+ Collation = new Collation(function);
+ }
+
+ public CollateAttribute(CollationFunction function, string customFunction)
+ {
+ Collation = new Collation(function, customFunction);
+ }
+
+ public Collation Collation { get; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Attributes/OnConflictAction.cs b/SQLite.CodeFirst/Public/Attributes/OnConflictAction.cs
new file mode 100644
index 0000000..e2df5b0
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/OnConflictAction.cs
@@ -0,0 +1,16 @@
+namespace SQLite.CodeFirst
+{
+ ///
+ /// The action to resolve a UNIQUE constraint violation.
+ /// Is used together with the .
+ ///
+ public enum OnConflictAction
+ {
+ None,
+ Rollback,
+ Abort,
+ Fail,
+ Ignore,
+ Replace
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Attributes/SqlDefaultValueAttribute.cs b/SQLite.CodeFirst/Public/Attributes/SqlDefaultValueAttribute.cs
new file mode 100644
index 0000000..6c45780
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/SqlDefaultValueAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Decorate an column with this attribute to create a "DEFAULT {defaultvalue}".
+ ///
+ /// https://www.sqlite.org/lang_createtable.html [05.10.2017]
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class SqlDefaultValueAttribute : Attribute
+ {
+ public string DefaultValue { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Attributes/UniqueAttribute.cs b/SQLite.CodeFirst/Public/Attributes/UniqueAttribute.cs
new file mode 100644
index 0000000..d51593c
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/UniqueAttribute.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// The UNIQUE Constraint prevents two records from having identical values in a particular column.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class UniqueAttribute : Attribute
+ {
+ public UniqueAttribute()
+ {
+ OnConflict = OnConflictAction.None;
+ }
+
+ public UniqueAttribute(OnConflictAction onConflict)
+ {
+ OnConflict = onConflict;
+ }
+
+ public OnConflictAction OnConflict { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Collation.cs b/SQLite.CodeFirst/Public/Collation.cs
new file mode 100644
index 0000000..0695e73
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Collation.cs
@@ -0,0 +1,46 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// This class can be used to specify the default collation for the database. Explicit Collate attributes will take precendence.
+ /// When SQLite compares two strings, it uses a collating sequence or collating function (two words for the same thing)
+ /// to determine which string is greater or if the two strings are equal. SQLite has three built-in collating functions (see ).
+ /// Set to and specify the name using the function parameter.
+ ///
+ public class Collation
+ {
+ public Collation()
+ : this(CollationFunction.None)
+ {
+ }
+
+ public Collation(CollationFunction function)
+ : this(function, null)
+ {
+ }
+
+ public Collation(CollationFunction function, string customFunction)
+ {
+ if (function != CollationFunction.Custom && !string.IsNullOrEmpty(customFunction))
+ {
+ throw new ArgumentException("If the collation is not set to CollationFunction.Custom a function must not be specified.", nameof(function));
+ }
+
+ if (function == CollationFunction.Custom && string.IsNullOrEmpty(customFunction))
+ {
+ throw new ArgumentException("If the collation is set to CollationFunction.Custom a function must be specified.", nameof(function));
+ }
+
+ CustomFunction = customFunction;
+ Function = function;
+ }
+
+ public CollationFunction Function { get; set; }
+
+ ///
+ /// The name of the custom collating function to use (CollationFunction.Custom).
+ ///
+ public string CustomFunction { get; set; }
+ }
+}
diff --git a/SQLite.CodeFirst/Public/CollationFunction.cs b/SQLite.CodeFirst/Public/CollationFunction.cs
new file mode 100644
index 0000000..6bd054d
--- /dev/null
+++ b/SQLite.CodeFirst/Public/CollationFunction.cs
@@ -0,0 +1,33 @@
+namespace SQLite.CodeFirst
+{
+ ///
+ /// The collation function to use for this column.
+ /// Is used together with the , and when setting a default collation for the database.
+ ///
+ public enum CollationFunction
+ {
+ None,
+
+ ///
+ /// The same as binary, except that trailing space characters are ignored.
+ ///
+ RTrim,
+
+ ///
+ /// The same as binary, except the 26 upper case characters of ASCII are folded to their lower case equivalents before
+ /// the comparison is performed. Note that only ASCII characters are case folded. SQLite does not attempt to do full
+ /// UTF case folding due to the size of the tables required.
+ ///
+ NoCase,
+
+ ///
+ /// Compares string data using memcmp(), regardless of text encoding.
+ ///
+ Binary,
+
+ ///
+ /// An application can register additional collating functions using the sqlite3_create_collation() interface.
+ ///
+ Custom
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/SqliteCreateDatabaseIfNotExists.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
similarity index 87%
rename from SQLite.CodeFirst/SqliteCreateDatabaseIfNotExists.cs
rename to SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
index 688ef17..c572be6 100644
--- a/SQLite.CodeFirst/SqliteCreateDatabaseIfNotExists.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
@@ -1,5 +1,6 @@
using System.Data.Entity;
using System.IO;
+using SQLite.CodeFirst.Utility;
namespace SQLite.CodeFirst
{
@@ -43,18 +44,7 @@ public override void InitializeDatabase(TContext context)
{
string databaseFilePath = GetDatabasePathFromContext(context);
- var fileInfo = new FileInfo(databaseFilePath);
-
- bool exists;
- if (nullByteFileMeansNotExisting)
- {
- exists = fileInfo.Exists && fileInfo.Length != 0;
- }
- else
- {
- exists = fileInfo.Exists;
- }
-
+ bool exists = InMemoryAwareFile.Exists(databaseFilePath, nullByteFileMeansNotExisting);
if (exists)
{
return;
diff --git a/SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
similarity index 74%
rename from SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs
rename to SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
index 91a8c19..3154835 100644
--- a/SQLite.CodeFirst/SqliteDropCreateDatabaseAlways.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
@@ -1,5 +1,6 @@
using System.Data.Entity;
using System.IO;
+using SQLite.CodeFirst.Utility;
namespace SQLite.CodeFirst
{
@@ -30,13 +31,18 @@ public override void InitializeDatabase(TContext context)
{
string databseFilePath = GetDatabasePathFromContext(context);
- bool dbExists = File.Exists(databseFilePath);
- if (dbExists)
+ bool exists = InMemoryAwareFile.Exists(databseFilePath);
+ if (exists)
{
- File.Delete(databseFilePath);
+ FileAttributes? attributes = InMemoryAwareFile.GetFileAttributes(databseFilePath);
+ InMemoryAwareFile.Delete(databseFilePath);
+ base.InitializeDatabase(context);
+ InMemoryAwareFile.SetFileAttributes(databseFilePath, attributes);
+ }
+ else
+ {
+ base.InitializeDatabase(context);
}
-
- base.InitializeDatabase(context);
}
}
}
diff --git a/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs
new file mode 100644
index 0000000..f8029fc
--- /dev/null
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs
@@ -0,0 +1,179 @@
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using System.IO;
+using System.Linq;
+using SQLite.CodeFirst.Utility;
+using System.Diagnostics.CodeAnalysis;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// An implementation of that will always recreate and optionally re-seed the
+ /// database the first time that a context is used in the app domain or if the model has changed.
+ /// To seed the database, create a derived class and override the Seed method.
+ ///
+ /// To detect model changes a new table (implementation of ) is added to the database.
+ /// There is one record in this table which holds the hash of the SQL-statement which was generated from the model
+ /// executed to create the database. When initializing the database the initializer checks if the hash of the SQL-statement for the
+ /// model is still the same as the hash in the database. If you use this initializer on a existing database, this initializer
+ /// will interpret this as model change because of the new table.
+ /// Notice that a database can be used by more than one context. Therefore the name of the context is saved as a part of the history record.
+ ///
+ ///
+ /// The type of the context.
+ public class SqliteDropCreateDatabaseWhenModelChanges : SqliteInitializerBase
+ where TContext : DbContext
+ {
+ private readonly Type historyEntityType;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder)
+ : base(modelBuilder)
+ {
+ historyEntityType = typeof(History);
+ ConfigureHistoryEntity();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// Type of the history entity (must implement and provide an parameterless constructor).
+ public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder, Type historyEntityType)
+ : base(modelBuilder)
+ {
+ this.historyEntityType = historyEntityType;
+ ConfigureHistoryEntity();
+ }
+
+
+ protected void ConfigureHistoryEntity()
+ {
+ HistoryEntityTypeValidator.EnsureValidType(historyEntityType);
+ ModelBuilder.RegisterEntityType(historyEntityType);
+ }
+
+ ///
+ /// Initialize the database for the given context.
+ /// Generates the SQLite-DDL from the model and executes it against the database.
+ /// After that the method is executed.
+ /// All actions are be executed in transactions.
+ ///
+ /// The context.
+ public override void InitializeDatabase(TContext context)
+ {
+ string databaseFilePath = GetDatabasePathFromContext(context);
+
+ bool dbExists = InMemoryAwareFile.Exists(databaseFilePath);
+ if (dbExists)
+ {
+ if (IsSameModel(context))
+ {
+ return;
+ }
+
+ FileAttributes? attributes = InMemoryAwareFile.GetFileAttributes(databaseFilePath);
+ CloseDatabase(context);
+ DeleteDatabase(context, databaseFilePath);
+ base.InitializeDatabase(context);
+ InMemoryAwareFile.SetFileAttributes(databaseFilePath, attributes);
+ SaveHistory(context);
+ }
+ else
+ {
+ base.InitializeDatabase(context);
+ SaveHistory(context);
+ }
+ }
+
+ ///
+ /// Called to drop/remove Database file from disk.
+ ///
+ /// The context.
+ /// Filename of Database to be removed.
+ protected virtual void DeleteDatabase(TContext context, string databaseFilePath)
+ {
+ InMemoryAwareFile.Delete(databaseFilePath);
+ }
+
+ [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.GC.Collect", Justification = "Required.")]
+ private static void CloseDatabase(TContext context)
+ {
+ context.Database.Connection.Close();
+ GC.Collect();
+ GC.WaitForPendingFinalizers();
+ }
+
+ private void SaveHistory(TContext context)
+ {
+ var hash = GetHashFromModel(context.Database.Connection);
+ var history = GetHistoryRecord(context);
+ EntityState entityState;
+ if (history == null)
+ {
+ history = (IHistory)Activator.CreateInstance(historyEntityType);
+ entityState = EntityState.Added;
+ }
+ else
+ {
+ entityState = EntityState.Modified;
+ }
+
+ history.Context = context.GetType().FullName;
+ history.Hash = hash;
+ history.CreateDate = DateTime.UtcNow;
+
+ context.Set(historyEntityType).Attach(history);
+ context.Entry(history).State = entityState;
+ context.SaveChanges();
+ }
+
+ [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ private bool IsSameModel(TContext context)
+ {
+
+ var hash = GetHashFromModel(context.Database.Connection);
+
+ try
+ {
+ var history = GetHistoryRecord(context);
+ return history?.Hash == hash;
+ }
+ catch (Exception)
+ {
+ // This happens if the history table does not exist.
+ // So it covers also the case with a null byte file (see SqliteCreateDatabaseIfNotExists).
+ return false;
+ }
+ }
+
+ private IHistory GetHistoryRecord(TContext context)
+ {
+ // Yes, it seams to be complicated but it has to be done this way
+ // in order to be supported by .NET 4.0.
+ DbQuery dbQuery = context.Set(historyEntityType).AsNoTracking();
+ IEnumerable records = Enumerable.Cast(dbQuery);
+ return records.SingleOrDefault();
+ }
+
+ private string GetHashFromModel(DbConnection connection)
+ {
+ var sql = GetSqlFromModel(connection);
+ string hash = HashCreator.CreateHash(sql);
+ return hash;
+ }
+
+ private string GetSqlFromModel(DbConnection connection)
+ {
+ var model = ModelBuilder.Build(connection);
+ var sqliteSqlGenerator = new SqliteSqlGenerator(DefaultCollation);
+ return sqliteSqlGenerator.Generate(model.StoreModel);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Public/DbInitializers/SqliteInitializerBase.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteInitializerBase.cs
new file mode 100644
index 0000000..1309b1f
--- /dev/null
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteInitializerBase.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using System.Data.Entity.ModelConfiguration.Conventions;
+using System.IO;
+using SQLite.CodeFirst.Convention;
+using SQLite.CodeFirst.Extensions;
+using SQLite.CodeFirst.Utility;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// An basic implementation of the interface.
+ /// This class provides common logic which can be used when writing an Sqlite-Initializer.
+ /// The logic provided is:
+ /// 1. Remove/Add specific conventions
+ /// 2. Get the path to the database file
+ /// 3. Create a new SQLite-database from the model (Code First)
+ /// 4. Seed data to the new created database
+ /// The following implementations are provided: ,
+ /// .
+ ///
+ /// The type of the context.
+ public abstract class SqliteInitializerBase : IDatabaseInitializer
+ where TContext : DbContext
+ {
+ protected SqliteInitializerBase(DbModelBuilder modelBuilder, Collation defaultCollation = null)
+ {
+ ModelBuilder = modelBuilder ?? throw new ArgumentNullException(nameof(modelBuilder));
+ DefaultCollation = defaultCollation;
+
+ // This convention will crash the SQLite Provider before "InitializeDatabase" gets called.
+ // See https://github.com/msallin/SQLiteCodeFirst/issues/7 for details.
+ modelBuilder.Conventions.Remove();
+
+ // There is some functionality which is supported by SQLite which can not be covered
+ // by using the data annotation attributes from the .net framework.
+ // So there are some custom attributes.
+ modelBuilder.RegisterAttributeAsColumnAnnotation();
+ modelBuilder.RegisterAttributeAsColumnAnnotation();
+ modelBuilder.RegisterAttributeAsColumnAnnotation();
+ modelBuilder.RegisterAttributeAsColumnAnnotation();
+
+ // By default there is a 'ForeignKeyIndexConvention' but it can be removed.
+ // And there is no "Contains" and no way to enumerate the ConventionsCollection.
+ // So a try/catch will do the job.
+ try
+ {
+ // Place the own ForeinKeyIndexConvention right after the original.
+ // The own convention will rename the automatically created indicies by using the correct scheme.
+ modelBuilder.Conventions.AddAfter(new SqliteForeignKeyIndexConvention());
+ }
+ catch (InvalidOperationException)
+ {
+ // Ignore it.
+ }
+ }
+
+ public Collation DefaultCollation { get; }
+
+ protected DbModelBuilder ModelBuilder { get; }
+
+ ///
+ /// Initialize the database for the given context.
+ /// Generates the SQLite-DDL from the model and executs it against the database.
+ /// After that the method is executed.
+ /// All actions are be executed in transactions.
+ ///
+ /// The context.
+ public virtual void InitializeDatabase(TContext context)
+ {
+ DbModel model = ModelBuilder.Build(context.Database.Connection);
+
+ string dbFile = GetDatabasePathFromContext(context);
+ InMemoryAwareFile.CreateDirectory(dbFile);
+
+ var sqliteDatabaseCreator = new SqliteDatabaseCreator(DefaultCollation);
+ sqliteDatabaseCreator.Create(context.Database, model);
+
+ Seed(context);
+ context.SaveChanges();
+ }
+
+ ///
+ /// Is executed right after the initialization .
+ /// Use this method to seed data into the empty database.
+ ///
+ /// The context.
+ protected virtual void Seed(TContext context)
+ {
+ }
+
+ ///
+ /// Gets the database path file path from a .
+ ///
+ /// The context to get the database file path from.
+ /// The full path to the SQLite database file.
+ protected string GetDatabasePathFromContext(TContext context)
+ {
+ return ConnectionStringParser.GetDataSource(context.Database.Connection.ConnectionString);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Entities/History.cs b/SQLite.CodeFirst/Public/Entities/History.cs
new file mode 100644
index 0000000..5238aee
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Entities/History.cs
@@ -0,0 +1,56 @@
+using System;
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Represents the database table "history" which is used to store the necessary information
+ /// to detect if the model has changed.
+ ///
+ [Table(nameof(History) + "_" + TableNamePostfix)]
+ public class History : IHistory
+ {
+ ///
+ /// The postfix which is appended to the history table name
+ /// to ensure that this table name is unique.
+ ///
+ public const string TableNamePostfix = "82c009b41631-48579635f1ff64eb62d9";
+
+ ///
+ /// Gets or sets the identifier of the record.
+ /// Is automatilcally generated by the database.
+ ///
+ ///
+ /// The identifier.
+ ///
+ [Key]
+ public int Id { get; set; }
+
+ ///
+ /// Gets or sets the hash which represents the current database structure/state.
+ ///
+ ///
+ /// The hash.
+ ///
+ [Required]
+ public string Hash { get; set; }
+
+ ///
+ /// Gets or sets the key of the DbContext to which this record belongs.
+ ///
+ ///
+ /// The context key.
+ ///
+ public string Context { get; set; }
+
+ ///
+ /// Gets or sets the create date of the record.
+ ///
+ ///
+ /// The create date.
+ ///
+ [Required]
+ public DateTime CreateDate { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Entities/IHistory.cs b/SQLite.CodeFirst/Public/Entities/IHistory.cs
new file mode 100644
index 0000000..cbaf2e3
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Entities/IHistory.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ public interface IHistory
+ {
+ int Id { get; set; }
+ string Hash { get; set; }
+ string Context { get; set; }
+ DateTime CreateDate { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/IDatabaseCreator.cs b/SQLite.CodeFirst/Public/IDatabaseCreator.cs
new file mode 100644
index 0000000..aef2a47
--- /dev/null
+++ b/SQLite.CodeFirst/Public/IDatabaseCreator.cs
@@ -0,0 +1,10 @@
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+
+namespace SQLite.CodeFirst
+{
+ public interface IDatabaseCreator
+ {
+ void Create(Database db, DbModel model);
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/ISqliteSqlGenerator.cs b/SQLite.CodeFirst/Public/ISqliteSqlGenerator.cs
new file mode 100644
index 0000000..c57c0d2
--- /dev/null
+++ b/SQLite.CodeFirst/Public/ISqliteSqlGenerator.cs
@@ -0,0 +1,9 @@
+using System.Data.Entity.Core.Metadata.Edm;
+
+namespace SQLite.CodeFirst
+{
+ public interface ISqlGenerator
+ {
+ string Generate(EdmModel storeModel);
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs b/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs
new file mode 100644
index 0000000..0626c24
--- /dev/null
+++ b/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Data.Entity;
+using System.Data.Entity.Infrastructure;
+using System.Diagnostics;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Creates a SQLite-Database based on a Entity Framework and .
+ /// This creator can be used standalone or within an initializer.
+ ///
+ /// The generated DDL-SQL will be executed together as one statement.
+ /// If there is a open transaction on the Database, the statement will be executed within this transaction.
+ /// Otherwise it will be executed within a own transaction. In anyway the atomicity is guaranteed.
+ ///
+ ///
+ public class SqliteDatabaseCreator : IDatabaseCreator
+ {
+ public SqliteDatabaseCreator(Collation defaultCollation = null)
+ {
+ DefaultCollation = defaultCollation;
+ }
+
+ public Collation DefaultCollation { get; }
+
+ ///
+ /// Creates the SQLite-Database.
+ ///
+ public void Create(Database db, DbModel model)
+ {
+ if (db == null) throw new ArgumentNullException("db");
+ if (model == null) throw new ArgumentNullException("model");
+
+ var sqliteSqlGenerator = new SqliteSqlGenerator(DefaultCollation);
+ string sql = sqliteSqlGenerator.Generate(model.StoreModel);
+ Debug.Write(sql);
+ db.ExecuteSqlCommand(TransactionalBehavior.EnsureTransaction, sql);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Public/SqliteSqlGenerator.cs b/SQLite.CodeFirst/Public/SqliteSqlGenerator.cs
new file mode 100644
index 0000000..944115e
--- /dev/null
+++ b/SQLite.CodeFirst/Public/SqliteSqlGenerator.cs
@@ -0,0 +1,29 @@
+using System.Data.Entity.Core.Metadata.Edm;
+using SQLite.CodeFirst.Builder;
+using SQLite.CodeFirst.Statement;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Generates the SQL statement to create a database, based on a .
+ ///
+ public class SqliteSqlGenerator : ISqlGenerator
+ {
+ public SqliteSqlGenerator(Collation defaultCollation = null)
+ {
+ DefaultCollation = defaultCollation;
+ }
+
+ public Collation DefaultCollation { get; }
+
+ ///
+ /// Generates the SQL statement, based on the .
+ ///
+ public string Generate(EdmModel storeModel)
+ {
+ IStatementBuilder statementBuilder = new CreateDatabaseStatementBuilder(storeModel, DefaultCollation);
+ IStatement statement = statementBuilder.BuildStatement();
+ return statement.CreateStatement();
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj b/SQLite.CodeFirst/SQLite.CodeFirst.csproj
index 844d66d..954af58 100644
--- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj
+++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj
@@ -1,126 +1,26 @@
-
-
-
+
+
- Debug-45
- AnyCPU
- {50A32FE4-0E13-4213-A373-72523CDF34D9}
- Library
- Properties
- SQLite.CodeFirst
- SQLite.CodeFirst
- 512
- true
-
-
- ..\
- true
-
-
-
-
- AnyCPU
- true
- DEBUG;TRACE
- full
- prompt
- MinimumRecommendedRules.ruleset
-
-
- v4.0
- bin\Debug\net40
-
-
- bin\Debug\net45
- v4.5
-
-
-
-
- AnyCPU
- TRACE
- true
- pdbonly
- prompt
- MinimumRecommendedRules.ruleset
-
-
- bin\Release\net40
- v4.0
-
-
- bin\Release\net45
- v4.5
-
-
-
-
-
-
-
-
- ..\packages\EntityFramework.6.1.3\lib\net40\EntityFramework.dll
-
-
-
-
- ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ net40;net45;netstandard2.1
+ Marc Sallin
+
+
+ Creates a SQLite Database from Code, using Entity Framework CodeFirst. This Project ships several IDbInitializer which creates a new SQLite Database, based on your model/code.
+ https://github.com/msallin/SQLiteCodeFirst
+ https://github.com/msallin/SQLiteCodeFirst
+ GitHub
+ true
+ https://github.com/msallin/SQLiteCodeFirst/blob/master/LICENSE
+ false
+ Copyright (C) Marc Sallin
+ SQLite EntityFramework EF CodeFirst
+ true
+ true
+ ..\Shared\SQLite.CodeFirst.StrongNameKey.snk
+
+
-
-
+
-
-
-
-
- This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
-
-
-
-
-
\ No newline at end of file
+
+
diff --git a/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec b/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec
deleted file mode 100644
index f34f826..0000000
--- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj.nuspec
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- $id$
- $version$
- $author$
- $author$
- https://github.com/msallin/SQLiteCodeFirst/blob/master/LICENSE
- https://github.com/msallin/SQLiteCodeFirst
- false
- $description$
- Creates a SQLite Database from Code, using Entity Framework CodeFirst.
-
- Copyright (C) Marc Sallin
- SQLite EntityFramework EF CodeFirst
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst/SqliteDatabaseCreator.cs b/SQLite.CodeFirst/SqliteDatabaseCreator.cs
deleted file mode 100644
index 11e5d62..0000000
--- a/SQLite.CodeFirst/SqliteDatabaseCreator.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System.Data.Entity;
-using System.Data.Entity.Infrastructure;
-using SQLite.CodeFirst.Builder;
-using SQLite.CodeFirst.Statement;
-
-namespace SQLite.CodeFirst
-{
- ///
- /// Creates a SQLite-Database based on a Entity Framework and .
- /// This creator can be used standalone or within an initializer.
- ///
- public class SqliteDatabaseCreator
- {
- private readonly Database db;
- private readonly DbModel model;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The database.
- /// The model.
- public SqliteDatabaseCreator(Database db, DbModel model)
- {
- this.db = db;
- this.model = model;
- }
-
- ///
- /// Creates the SQLite-Database.
- ///
- public void Create()
- {
- IStatementBuilder statementBuilder = new CreateDatabaseStatementBuilder(model.StoreModel);
- IStatement statement = statementBuilder.BuildStatement();
- string sql = statement.CreateStatement();
- db.ExecuteSqlCommand(sql);
- }
- }
-}
diff --git a/SQLite.CodeFirst/SqliteInitializerBase.cs b/SQLite.CodeFirst/SqliteInitializerBase.cs
deleted file mode 100644
index 25b571f..0000000
--- a/SQLite.CodeFirst/SqliteInitializerBase.cs
+++ /dev/null
@@ -1,116 +0,0 @@
-using System;
-using System.Data.Entity;
-using System.Data.Entity.ModelConfiguration.Conventions;
-using SQLite.CodeFirst.Convention;
-using System.IO;
-
-namespace SQLite.CodeFirst
-{
- ///
- /// An basic implementation of the interface.
- /// This class provides common logic which can be used when writing an Sqlite-Initializer.
- /// The logic provided is:
- /// 1. Remove/Add specific Conventions
- /// 2. Get the path to the database file
- /// 3. Create a new SQLite-Database from the model (Code First)
- /// 4. Seed data to the new created database
- /// The following implementations are provided: , .
- ///
- /// The type of the context.
- public abstract class SqliteInitializerBase : IDatabaseInitializer
- where TContext : DbContext
- {
- private readonly DbModelBuilder modelBuilder;
-
- protected SqliteInitializerBase(DbModelBuilder modelBuilder)
- {
- if (modelBuilder == null)
- {
- throw new ArgumentNullException("modelBuilder");
- }
-
- this.modelBuilder = modelBuilder;
-
- // This convention will crash the SQLite Provider before "InitializeDatabase" gets called.
- // See https://github.com/msallin/SQLiteCodeFirst/issues/7 for details.
- modelBuilder.Conventions.Remove();
-
- // By default there is a 'ForeignKeyIndexConvention' but it can be removed.
- // And there is no "Contains" and no way to enumerate the ConventionsCollection.
- // So a try/catch will do the job.
- try
- {
- // Place the own ForeinKeyIndexConvention right after the original.
- // The own convention will rename the automatically created indicies by using the correct scheme.
- modelBuilder.Conventions.AddAfter(new SqliteForeignKeyIndexConvention());
- }
- catch (InvalidOperationException exception)
- {
- // Ignore it.
- }
- }
-
- ///
- /// Initialize the database for the given context.
- /// Generates the SQLite-DDL from the model and executs it against the database.
- /// After that the method is executed.
- /// All actions are be executed in transactions.
- ///
- /// The context.
- public virtual void InitializeDatabase(TContext context)
- {
- var model = modelBuilder.Build(context.Database.Connection);
-
- var dbFile = GetDatabasePathFromContext(context);
- var dbFileInfo = new FileInfo(dbFile);
- dbFileInfo.Directory.Create();
-
- using (var transaction = context.Database.BeginTransaction())
- {
- try
- {
- var sqliteDatabaseCreator = new SqliteDatabaseCreator(context.Database, model);
- sqliteDatabaseCreator.Create();
- transaction.Commit();
- }
- catch (Exception)
- {
- transaction.Rollback();
- throw;
- }
- }
-
- using (var transaction = context.Database.BeginTransaction())
- {
- try
- {
- Seed(context);
- context.SaveChanges();
- transaction.Commit();
- }
- catch (Exception)
- {
- transaction.Rollback();
- throw;
- }
- }
- }
-
- ///
- /// Is executed right after the initialization .
- /// Use this method to seed data into the empty database.
- ///
- /// The context.
- protected virtual void Seed(TContext context) { }
-
- ///
- /// Gets the database path file path from a .
- ///
- /// The context to get the database file path from.
- /// The full path to the SQLite database file.
- protected string GetDatabasePathFromContext(TContext context)
- {
- return SqliteConnectionStringParser.GetDataSource(context.Database.Connection.ConnectionString);
- }
- }
-}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Utility/AssociationTypeWrapper.cs b/SQLite.CodeFirst/Utility/AssociationTypeWrapper.cs
deleted file mode 100644
index c7c1a58..0000000
--- a/SQLite.CodeFirst/Utility/AssociationTypeWrapper.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System.Data.Entity.Core.Metadata.Edm;
-
-namespace SQLite.CodeFirst.Utility
-{
- internal class AssociationTypeWrapper
- {
- public AssociationType AssociationType { get; set; }
- public string FromTableName { get; set; }
- public string ToTableName { get; set; }
- }
-}
diff --git a/SQLite.CodeFirst/packages.config b/SQLite.CodeFirst/packages.config
deleted file mode 100644
index 57e2eaa..0000000
--- a/SQLite.CodeFirst/packages.config
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Shared/SQLite.CodeFirst.StrongNameKey.snk b/Shared/SQLite.CodeFirst.StrongNameKey.snk
new file mode 100644
index 0000000..7a0d9a6
Binary files /dev/null and b/Shared/SQLite.CodeFirst.StrongNameKey.snk differ
diff --git a/Shared/SQLite.CodeFirst.ruleset b/Shared/SQLite.CodeFirst.ruleset
new file mode 100644
index 0000000..5b02797
--- /dev/null
+++ b/Shared/SQLite.CodeFirst.ruleset
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ci_appveyor.yml b/ci_appveyor.yml
new file mode 100644
index 0000000..36b0af4
--- /dev/null
+++ b/ci_appveyor.yml
@@ -0,0 +1,23 @@
+version: CI_{branch}_{build}
+image: Visual Studio 2022
+skip_tags: true
+configuration:
+- Release
+- Debug
+platform: Any CPU
+before_build:
+- ps: dotnet --version
+- ps: dotnet restore
+build:
+ project: SQLite.CodeFirst.sln
+ parallel: true
+ verbosity: normal
+artifacts:
+- path: SQLite.CodeFirst\bin\Debug\**\SQLite.CodeFirst.dll
+ name: Debug
+- path: SQLite.CodeFirst\bin\Release\**\SQLite.CodeFirst.dll
+ name: Release
+- path: SQLite.CodeFirst\bin\Release\*.nupkg
+ name: NuPkgDebug
+- path: SQLite.CodeFirst\bin\Debug\*.nupkg
+ name: NuPkgRelease
\ No newline at end of file
diff --git a/release_appveyor.yml b/release_appveyor.yml
index c7581e6..be4baa2 100644
--- a/release_appveyor.yml
+++ b/release_appveyor.yml
@@ -1,34 +1,36 @@
-version: 0.9.{build}.0
+version: 1.7.0.{build}
+image: Visual Studio 2022
branches:
only:
- master
skip_tags: true
platform: Any CPU
-assembly_info:
+configuration: Release
+dotnet_csproj:
patch: true
- file: '**\AssemblyInfo.*'
+ file: '**\*.csproj'
+ version: '{version}'
+ package_version: '{version}'
assembly_version: '{version}'
- assembly_file_version: '{version}'
- assembly_informational_version: '{version}'
+ file_version: '{version}'
+ informational_version: '{version}'
+before_build:
+- ps: dotnet --version
+- ps: dotnet restore
build:
- project: BuildAllConfigurations.proj
+ project: SQLite.CodeFirst.sln
+ parallel: true
verbosity: normal
-after_build:
-- ps: >-
- cd $env:APPVEYOR_BUILD_FOLDER
-
- cd '.nuget'
-
- ./nuget.exe pack '..\SQLite.CodeFirst\SQLite.CodeFirst.csproj' -Properties -Symbols -OutputDirectory '..\SQLite.CodeFirst\bin'
artifacts:
- path: SQLite.CodeFirst\bin
name: Assemblies
-- path: SQLite.CodeFirst\bin\*.nupkg
+- path: SQLite.CodeFirst\bin\Release\*.nupkg
name: NuPkg
deploy:
+# Encrypt with https://ci.appveyor.com/tools/encrypt
- provider: NuGet
api_key:
- secure: QmbFnerlfTAFUZpnaPgVDywMH4fF8rVakefmqvhu3qm9SpuDlLGB9S4HwtdE3Nep
+ secure: fdOvSLLttfWbXxkmrgNG+jfczNAqx0HOIqYeVhToHGvezVwts758wz+sbGkv2RhZ
on:
branch: master
- provider: GitHub
@@ -40,10 +42,4 @@ deploy:
artifact: Assemblies,NuPkg
draft: true
on:
- branch: master
-notifications:
-- provider: Webhook
- url: https://zapier.com/hooks/catch/b3cbz0/
- on_build_success: true
- on_build_failure: true
- on_build_status_changed: false
\ No newline at end of file
+ branch: master
\ No newline at end of file