diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..0905865
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,36 @@
+# Dependabot keeps the .NET SDK (global.json), NuGet packages and GitHub Actions versions up to date.
+#
+# Minor and patch updates are grouped into a single PR per ecosystem to cut noise; major updates open
+# their own PR so the breaking-change surface stays visible. Updates are proposed as PRs and merged
+# manually after the CI "Build & Test" check passes.
+version: 2
+updates:
+ # Bumps the SDK version pinned in global.json, which setup-dotnet installs in CI. The dotnet-sdk
+ # ecosystem only does version updates (no security updates) and targets global.json files.
+ - package-ecosystem: dotnet-sdk
+ directory: "/"
+ schedule:
+ interval: weekly
+ open-pull-requests-limit: 10
+
+ - package-ecosystem: nuget
+ directory: "/"
+ schedule:
+ interval: weekly
+ open-pull-requests-limit: 10
+ groups:
+ nuget-minor-and-patch:
+ update-types:
+ - minor
+ - patch
+
+ - package-ecosystem: github-actions
+ directory: "/"
+ schedule:
+ interval: weekly
+ open-pull-requests-limit: 10
+ groups:
+ actions-minor-and-patch:
+ update-types:
+ - minor
+ - patch
diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
new file mode 100644
index 0000000..59f6664
--- /dev/null
+++ b/.github/workflows/cd.yml
@@ -0,0 +1,136 @@
+name: CD
+
+# Builds, tests and packs the NuGet package on a version tag (v1.2.3) or manual run.
+# Publishing to NuGet uses Trusted Publishing (OIDC) and runs only on a v*.*.* tag push; a manual
+# run packs and uploads the artifact as a dry run without publishing (see the step comments below).
+
+on:
+ push:
+ tags: [ 'v*.*.*' ]
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Package version, e.g. 1.2.3. Defaults to a dev version when omitted.'
+ required: false
+
+permissions:
+ contents: read
+
+jobs:
+ pack-publish:
+ name: Pack & Publish
+ # Windows is required: the library multi-targets net40/net45 and the demo targets net48, which
+ # need the .NET Framework reference assemblies / targeting packs available on the Windows runners.
+ runs-on: windows-latest
+ timeout-minutes: 15
+ permissions:
+ id-token: write # Required for NuGet Trusted Publishing; without it NuGet/login fails with 403.
+ contents: write # Required to create the GitHub Release; a job-level block drops inherited defaults.
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ # Install the exact SDK pinned in global.json; Dependabot's dotnet-sdk updater keeps it current.
+ global-json-file: global.json
+ # Cache the restored NuGet packages, keyed on the committed lock files.
+ cache: true
+ cache-dependency-path: '**/packages.lock.json'
+
+ - name: Resolve version
+ id: version
+ shell: bash
+ # The manual input is read through an env var, not interpolated into the script, so a crafted
+ # value cannot inject shell. It is then validated as SemVer before anything downstream uses it.
+ env:
+ VERSION_INPUT: ${{ github.event.inputs.version }}
+ run: |
+ if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
+ VERSION="${GITHUB_REF_NAME#v}"
+ elif [[ -n "${VERSION_INPUT}" ]]; then
+ VERSION="${VERSION_INPUT}"
+ if ! [[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$ ]]; then
+ echo "Invalid version '${VERSION}'; expected SemVer such as 1.2.3 or 1.2.3-rc.1." >&2
+ exit 1
+ fi
+ else
+ VERSION="0.0.0-dev.${GITHUB_RUN_NUMBER}"
+ fi
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "Resolved package version: ${VERSION}"
+
+ - name: Restore
+ # Locked-mode restore fails the build if any packages.lock.json is missing or out of date, so the
+ # restore is reproducible and a forgotten lock-file update cannot slip through.
+ run: dotnet restore SQLite.CodeFirst.slnx --locked-mode
+
+ - name: Build
+ run: dotnet build SQLite.CodeFirst.slnx -c Release --no-restore -p:Version=${{ steps.version.outputs.version }}
+
+ - name: Test
+ # The test project runs on Microsoft.Testing.Platform (opted in via global.json), so target the
+ # solution with the native MTP dotnet test syntax.
+ run: dotnet test --solution SQLite.CodeFirst.slnx -c Release --no-build
+
+ - name: Pack
+ run: >
+ dotnet pack SQLite.CodeFirst/SQLite.CodeFirst.csproj -c Release --no-build
+ -p:Version=${{ steps.version.outputs.version }}
+ -o ${{ github.workspace }}/artifacts
+
+ - name: Upload package artifact
+ uses: actions/upload-artifact@v7
+ with:
+ name: nuget-package
+ path: |
+ ${{ github.workspace }}/artifacts/*.nupkg
+ ${{ github.workspace }}/artifacts/*.snupkg
+
+ # --- NuGet publishing via Trusted Publishing (OIDC). ---
+ # No long-lived API key: NuGet/login swaps the GitHub OIDC token for a short-lived key, authorized
+ # by the trusted publishing policy on nuget.org (the policy must name this workflow file, cd.yml).
+ # Gated to tag pushes so a manual run stays a dry run and never pushes the 0.0.0-dev.* version.
+ # https://learn.microsoft.com/nuget/nuget-org/trusted-publishing
+ - name: NuGet login (OIDC)
+ id: nuget-login
+ if: startsWith(github.ref, 'refs/tags/v')
+ uses: NuGet/login@v1
+ with:
+ user: ${{ secrets.NUGET_USER }} # nuget.org account/profile name, NOT the email address.
+
+ - name: Publish to NuGet
+ if: startsWith(github.ref, 'refs/tags/v')
+ shell: pwsh
+ # Resolve the package explicitly: on the Windows runner the default PowerShell shell does not
+ # expand a "*.nupkg" wildcard for dotnet, so a literal glob path is passed through and not found.
+ # Pushing the .nupkg also pushes the sibling .snupkg symbol package to nuget.org automatically.
+ run: |
+ $package = Get-ChildItem -Path "${{ github.workspace }}/artifacts/*.nupkg" | Select-Object -First 1
+ dotnet nuget push $package.FullName `
+ --api-key "${{ steps.nuget-login.outputs.NUGET_API_KEY }}" `
+ --source https://api.nuget.org/v3/index.json `
+ --skip-duplicate
+
+ # --- GitHub Release. ---
+ # Created only on tag pushes, after a successful NuGet publish, so the Release reflects what was
+ # actually shipped. Uses the gh CLI (preinstalled on the runner) rather than a third-party action.
+ # --generate-notes builds the notes from commits/PRs since the previous tag; --verify-tag guards
+ # against creating a release for a tag the runner cannot see. The .nupkg/.snupkg are attached so
+ # the package is downloadable straight from the Release page, not just the workflow run.
+ - name: Create GitHub Release
+ if: startsWith(github.ref, 'refs/tags/v')
+ shell: pwsh
+ # The tag is passed through an env var rather than interpolated into the script, matching the
+ # version step above. Assets are resolved with Get-ChildItem so the wildcards expand on the
+ # Windows runner (bash would mangle the backslashes in the workspace path).
+ env:
+ GH_TOKEN: ${{ github.token }}
+ TAG: ${{ github.ref_name }}
+ run: |
+ $assets = Get-ChildItem -Path "${{ github.workspace }}/artifacts/*.nupkg", "${{ github.workspace }}/artifacts/*.snupkg" | ForEach-Object { $_.FullName }
+ # SemVer pre-release tags carry a hyphen (e.g. v1.2.3-rc.1); mark those as GitHub pre-releases.
+ $ghArgs = @($env:TAG, '--title', $env:TAG, '--generate-notes', '--verify-tag')
+ if ($env:TAG -like '*-*') { $ghArgs += '--prerelease' }
+ $ghArgs += $assets
+ gh release create $ghArgs
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..9ecf110
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build-test:
+ name: Build & Test
+ # Windows is required: the library multi-targets net40/net45 and the demo targets net48, which
+ # need the .NET Framework reference assemblies / targeting packs available on the Windows runners.
+ runs-on: windows-latest
+ timeout-minutes: 15
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ # Install the exact SDK pinned in global.json; Dependabot's dotnet-sdk updater keeps it current.
+ global-json-file: global.json
+ # Cache the restored NuGet packages, keyed on the committed lock files.
+ cache: true
+ cache-dependency-path: '**/packages.lock.json'
+
+ - name: Restore
+ # Locked-mode restore fails the build if any packages.lock.json is missing or out of date, so the
+ # restore is reproducible and a forgotten lock-file update cannot slip through.
+ run: dotnet restore SQLite.CodeFirst.slnx --locked-mode
+
+ - name: Build
+ run: dotnet build SQLite.CodeFirst.slnx -c Release --no-restore
+
+ - name: Test
+ # The test project runs on Microsoft.Testing.Platform (opted in via global.json), so this uses the
+ # native MTP dotnet test syntax: --solution, and the TRX report flags instead of --logger trx.
+ run: >
+ dotnet test --solution SQLite.CodeFirst.slnx -c Release --no-build
+ --report-trx --report-trx-filename test-results.trx
+ --results-directory ${{ github.workspace }}/TestResults
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v7
+ with:
+ name: test-results
+ path: ${{ github.workspace }}/TestResults/*.trx
+ if-no-files-found: ignore
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..4dcd0ff
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,11 @@
+
+
+
+
+ true
+
+
+
diff --git a/README.md b/README.md
index 21e5e9f..a6b9367 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
# SQLite CodeFirst
-**Release Build** [](https://ci.appveyor.com/project/msallin/sqlitecodefirst-nv6vn/branch/master)
-
-**CI Build** [](https://ci.appveyor.com/project/msallin/sqlitecodefirst)
+[](https://github.com/msallin/SQLiteCodeFirst/actions/workflows/ci.yml)
+[](https://github.com/msallin/SQLiteCodeFirst/actions/workflows/cd.yml)
Creates a [SQLite Database](https://sqlite.org/) from Code, using [Entity Framework](https://msdn.microsoft.com/en-us/data/ef.aspx) CodeFirst.
@@ -12,6 +11,14 @@ To support this project you can: *star the repository*, report bugs/request feat
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 gap.
+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.
@@ -21,38 +28,34 @@ The following features are 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')
+- ForeignKey constraint (1-n relationships, support for 'Cascade on delete' and 'Cascade on update')
- Not Null constraint
-- Auto increment (An int PrimaryKey will automatically be incremented and you can explicit set the "AUTOINCREMENT" constraint to a PrimaryKey using the Autoincrement-Attribute)
+- Auto increment (An int PrimaryKey will automatically be incremented and you can explicitly 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.)
+- Collate constraint (Decorate columns with the `CollateAttribute`, which is part of this library. Use `CollationFunction.Custom` to specify your 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)
+- Cascade on update (Decorate the foreign key property with the `CascadeOnUpdateAttribute`, which is part of this library. Entity Framework cannot express `ON UPDATE CASCADE`, so this opt-in attribute must be placed on the dependent foreign key property, e.g. `TeamId`, not on the navigation property. It requires an explicit foreign key property.)
## 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 (uses net40)
-- .NET 4.5 (uses net45)
-- .NET 4.5.1 (uses net45)
-- .NET 4.5.2 (uses net45)
-- .NET 4.6 (uses net45)
-- .NET 4.6.1 (uses net45)
-- .NET 4.6.2 (uses net45)
-- .NET 4.7 (uses net45)
-- .NET 4.7.1 (uses net45)
-- .NET 4.7.2 (uses net45)
-- .NET 4.8 (uses net45)
+- .NET 4.5-4.8 (uses net45)
+- .NET Core 3.0-3.1 (uses netstandard2.1)
+- .NET 5 and later, including .NET 10 (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
+- SqliteCreateDatabaseIfNotExists
- SqliteDropCreateDatabaseAlways
- SqliteDropCreateDatabaseWhenModelChanges
@@ -68,7 +71,7 @@ public class MyDbContext : DbContext
{
public MyDbContext()
: base("ConnectionStringName") { }
-
+
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var sqliteConnectionInitializer = new SqliteCreateDatabaseIfNotExists(modelBuilder);
@@ -77,7 +80,7 @@ public class MyDbContext : DbContext
}
```
-Notice that the `SqliteDropCreateDatabaseWhenModelChanges<>` initializer will create a additional table in your database.
+Notice that the `SqliteDropCreateDatabaseWhenModelChanges<>` initializer will create an 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.
@@ -126,17 +129,55 @@ public class MyContext : DbContext
}
```
+### .NET Core example
+
+Add the following package references.
+```xml
+
+
+```
+
+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 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.
+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 get an exception when you try to run the console project.
In this case please check the following issue:
## Recognition
diff --git a/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs b/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs
index 76c4ec4..2301500 100644
--- a/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs
+++ b/SQLite.CodeFirst.Console/Entity/FooCompositeKey.cs
@@ -4,22 +4,22 @@
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; }
+ ///
+ /// 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; }
+ [Key, Column(Order = 2), StringLength(20)]
+ public string Version { get; set; }
- [StringLength(255)]
- public string Name { get; set; }
+ [StringLength(255)]
+ public string Name { get; set; }
- public virtual ICollection FooeyACollection { get; set; }
+ public virtual ICollection FooeyACollection { get; set; }
- public virtual ICollection FooeyBCollection { 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
index 09ec0cb..38baaf2 100644
--- a/SQLite.CodeFirst.Console/Entity/FooRelationshipA.cs
+++ b/SQLite.CodeFirst.Console/Entity/FooRelationshipA.cs
@@ -3,16 +3,16 @@
namespace SQLite.CodeFirst.Console.Entity
{
- ///
- /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
- ///
- public class FooRelationshipA
- {
- public int Id { get; set; }
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipA
+ {
+ public int Id { get; set; }
- [StringLength(255)]
- public string Name { get; set; }
+ [StringLength(255)]
+ public string Name { get; set; }
- public virtual ICollection Fooey { 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
index 907dcdb..271d8f8 100644
--- a/SQLite.CodeFirst.Console/Entity/FooRelationshipB.cs
+++ b/SQLite.CodeFirst.Console/Entity/FooRelationshipB.cs
@@ -3,16 +3,16 @@
namespace SQLite.CodeFirst.Console.Entity
{
- ///
- /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
- ///
- public class FooRelationshipB
- {
- public int Id { get; set; }
+ ///
+ /// See https://github.com/msallin/SQLiteCodeFirst/issues/109
+ ///
+ public class FooRelationshipB
+ {
+ public int Id { get; set; }
- [StringLength(255)]
- public string Name { get; set; }
+ [StringLength(255)]
+ public string Name { get; set; }
- public virtual ICollection Fooey { get; set; }
- }
+ public virtual ICollection Fooey { 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/SQLite.CodeFirst.Console.csproj b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
index 5ac83b7..95f93db 100644
--- a/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
+++ b/SQLite.CodeFirst.Console/SQLite.CodeFirst.Console.csproj
@@ -10,11 +10,7 @@
..\Shared\SQLite.CodeFirst.StrongNameKey.snk
-
-
-
-
-
+
diff --git a/SQLite.CodeFirst.Console/packages.lock.json b/SQLite.CodeFirst.Console/packages.lock.json
new file mode 100644
index 0000000..fe32642
--- /dev/null
+++ b/SQLite.CodeFirst.Console/packages.lock.json
@@ -0,0 +1,55 @@
+{
+ "version": 1,
+ "dependencies": {
+ ".NETFramework,Version=v4.8": {
+ "System.Data.SQLite": {
+ "type": "Direct",
+ "requested": "[1.0.119, )",
+ "resolved": "1.0.119",
+ "contentHash": "JSOJpnBf9goMnxGTJFGCmm6AffxgtpuXNXV5YvWO8UNC2zwd12qkUe5lAbnY+2ohIkIukgIjbvR1RA/sWILv3w==",
+ "dependencies": {
+ "System.Data.SQLite.Core": "[1.0.119]",
+ "System.Data.SQLite.EF6": "[1.0.119]",
+ "System.Data.SQLite.Linq": "[1.0.119]"
+ }
+ },
+ "EntityFramework": {
+ "type": "Transitive",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA=="
+ },
+ "Stub.System.Data.SQLite.Core.NetFramework": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "8b4SbSXAxXJ8tfn6bnBu0m+HXMZvkE+BfogUKITRFm+snxfT5BTK7fLZaAsNoFX8DkBBjtCEDd+ajWkcyu55QA=="
+ },
+ "System.Data.SQLite.Core": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "bhQB8HVtRA+OOYw8UTD1F1kU+nGJ0/OZvH1JmlVUI4bGvgVEWeX1NcHjA765NvUoRVuCPlt8PrEpZ1thSsk1jg==",
+ "dependencies": {
+ "Stub.System.Data.SQLite.Core.NetFramework": "[1.0.119]"
+ }
+ },
+ "System.Data.SQLite.EF6": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "BwwgCSeA80gsxdXtU7IQEBrN9kQXWQrD11hNYOJZbXBBI1C4r7hA4QhBAalO1nzijXikthGRUADIEMI3nlucLA==",
+ "dependencies": {
+ "EntityFramework": "6.4.4"
+ }
+ },
+ "System.Data.SQLite.Linq": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "tkzb9aKEiQd18UW8VS6+vGtrkhcp4mjHg/AAKwNN8u7aWoBswquOnU07016MfTXyBUAR67mepWUhFC8zqNdPnA=="
+ },
+ "sqlite.codefirst": {
+ "type": "Project",
+ "dependencies": {
+ "EntityFramework": "[6.5.2, )"
+ }
+ }
+ }
+ }
+}
\ 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..d8dbac3
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/SQLite.CodeFirst.NetCore.Console.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net10.0
+ true
+ ..\Shared\SQLite.CodeFirst.StrongNameKey.snk
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SQLite.CodeFirst.NetCore.Console/packages.lock.json b/SQLite.CodeFirst.NetCore.Console/packages.lock.json
new file mode 100644
index 0000000..c50d503
--- /dev/null
+++ b/SQLite.CodeFirst.NetCore.Console/packages.lock.json
@@ -0,0 +1,142 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net10.0": {
+ "System.Data.SQLite": {
+ "type": "Direct",
+ "requested": "[1.0.119, )",
+ "resolved": "1.0.119",
+ "contentHash": "JSOJpnBf9goMnxGTJFGCmm6AffxgtpuXNXV5YvWO8UNC2zwd12qkUe5lAbnY+2ohIkIukgIjbvR1RA/sWILv3w==",
+ "dependencies": {
+ "System.Data.SQLite.Core": "[1.0.119]",
+ "System.Data.SQLite.EF6": "[1.0.119]"
+ }
+ },
+ "System.Data.SQLite.EF6": {
+ "type": "Direct",
+ "requested": "[1.0.119, )",
+ "resolved": "1.0.119",
+ "contentHash": "BwwgCSeA80gsxdXtU7IQEBrN9kQXWQrD11hNYOJZbXBBI1C4r7hA4QhBAalO1nzijXikthGRUADIEMI3nlucLA==",
+ "dependencies": {
+ "EntityFramework": "6.4.4"
+ }
+ },
+ "EntityFramework": {
+ "type": "Transitive",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA==",
+ "dependencies": {
+ "System.CodeDom": "6.0.0",
+ "System.ComponentModel.Annotations": "5.0.0",
+ "System.Configuration.ConfigurationManager": "6.0.1",
+ "System.Data.SqlClient": "4.8.6"
+ }
+ },
+ "Microsoft.Win32.SystemEvents": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
+ },
+ "runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==",
+ "dependencies": {
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
+ }
+ },
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
+ },
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
+ },
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
+ },
+ "Stub.System.Data.SQLite.Core.NetStandard": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "dI7ngiCNgdm+n00nQvFTa+LbHvE9MIQXwMSLRzJI/KAJ7G1WmCachsvfE1CD6xvb3OXJvYYEfv3+S/LHyhN0Rg=="
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.ComponentModel.Annotations": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
+ },
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "6.0.1",
+ "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "6.0.0",
+ "System.Security.Permissions": "6.0.0"
+ }
+ },
+ "System.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "4.8.6",
+ "contentHash": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==",
+ "dependencies": {
+ "runtime.native.System.Data.SqlClient.sni": "4.7.0"
+ }
+ },
+ "System.Data.SQLite.Core": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "bhQB8HVtRA+OOYw8UTD1F1kU+nGJ0/OZvH1JmlVUI4bGvgVEWeX1NcHjA765NvUoRVuCPlt8PrEpZ1thSsk1jg==",
+ "dependencies": {
+ "Stub.System.Data.SQLite.Core.NetStandard": "[1.0.119]"
+ }
+ },
+ "System.Drawing.Common": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
+ "dependencies": {
+ "Microsoft.Win32.SystemEvents": "6.0.0"
+ }
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
+ "dependencies": {
+ "System.Windows.Extensions": "6.0.0"
+ }
+ },
+ "System.Windows.Extensions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
+ "dependencies": {
+ "System.Drawing.Common": "6.0.0"
+ }
+ },
+ "sqlite.codefirst": {
+ "type": "Project",
+ "dependencies": {
+ "EntityFramework": "[6.5.2, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/CascadeOnUpdateTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/CascadeOnUpdateTest.cs
new file mode 100644
index 0000000..1e7b45c
--- /dev/null
+++ b/SQLite.CodeFirst.Test/IntegrationTests/CascadeOnUpdateTest.cs
@@ -0,0 +1,125 @@
+using System.Collections.Generic;
+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;
+
+namespace SQLite.CodeFirst.Test.IntegrationTests
+{
+ ///
+ /// Verifies that the placed on a foreign key property
+ /// results in 'ON UPDATE CASCADE' being emitted for the corresponding foreign key constraint.
+ /// This exercises the full pipeline: the attribute is registered as a column annotation, survives
+ /// the EF model build and is read back from the store model when the SQL is generated.
+ ///
+ [TestClass]
+ public class CascadeOnUpdateTest
+ {
+ private static string generatedSql;
+
+ [TestMethod]
+ public void CascadeOnUpdateAttributeEmitsOnUpdateCascade()
+ {
+ 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 CascadeDbContext(connection))
+ {
+ // Touching the set forces the initializer (and therefore the SQL generation) to run.
+ context.Set().FirstOrDefault();
+ }
+ }
+
+ // Decorated FK with cascade-on-delete: both keywords, delete before update.
+ StringAssert.Contains(generatedSql, "FOREIGN KEY ([CustomerId]) REFERENCES \"Customers\"([Id]) ON DELETE CASCADE ON UPDATE CASCADE");
+
+ // Decorated FK without cascade-on-delete: only the update keyword.
+ StringAssert.Contains(generatedSql, "FOREIGN KEY ([WarehouseId]) REFERENCES \"Warehouses\"([Id]) ON UPDATE CASCADE");
+
+ // Undecorated FK: no cascade keyword at all.
+ Assert.IsFalse(generatedSql.Contains("[RegionId]) REFERENCES \"Regions\"([Id]) ON"),
+ "The foreign key without the attribute must not emit any cascade clause.");
+ }
+
+ private class Customer
+ {
+ public int Id { get; set; }
+ }
+
+ private class Warehouse
+ {
+ public int Id { get; set; }
+ }
+
+ private class Region
+ {
+ public int Id { get; set; }
+ }
+
+ private class Order
+ {
+ public int Id { get; set; }
+
+ [CascadeOnUpdate]
+ public int CustomerId { get; set; }
+ public Customer Customer { get; set; }
+
+ [CascadeOnUpdate]
+ public int? WarehouseId { get; set; }
+ public Warehouse Warehouse { get; set; }
+
+ public int? RegionId { get; set; }
+ public Region Region { get; set; }
+ }
+
+ private class CascadeDbContext : DbContext
+ {
+ public CascadeDbContext(DbConnection connection)
+ : base(connection, false)
+ {
+ }
+
+ protected override void OnModelCreating(DbModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity()
+ .HasRequired(o => o.Customer)
+ .WithMany()
+ .HasForeignKey(o => o.CustomerId)
+ .WillCascadeOnDelete(true);
+
+ modelBuilder.Entity()
+ .HasOptional(o => o.Warehouse)
+ .WithMany()
+ .HasForeignKey(o => o.WarehouseId)
+ .WillCascadeOnDelete(false);
+
+ modelBuilder.Entity()
+ .HasOptional(o => o.Region)
+ .WithMany()
+ .HasForeignKey(o => o.RegionId)
+ .WillCascadeOnDelete(false);
+
+ Database.SetInitializer(new CaptureInitializer(modelBuilder));
+ }
+ }
+
+ private class CaptureInitializer : SqliteInitializerBase
+ {
+ public CaptureInitializer(DbModelBuilder modelBuilder)
+ : base(modelBuilder)
+ {
+ }
+
+ public override void InitializeDatabase(CascadeDbContext context)
+ {
+ DbModel model = ModelBuilder.Build(context.Database.Connection);
+ generatedSql = new SqliteSqlGenerator().Generate(model.StoreModel);
+ base.InitializeDatabase(context);
+ }
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs
index c707341..d5ef6ab 100644
--- a/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs
+++ b/SQLite.CodeFirst.Test/IntegrationTests/InMemoryDbCreationTest.cs
@@ -1,8 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
-using SQLite.CodeFirst.Console;
-using SQLite.CodeFirst.Console.Entity;
+using SQLite.CodeFirst.NetCore.Console;
+using SQLite.CodeFirst.NetCore.Console.Entity;
namespace SQLite.CodeFirst.Test.IntegrationTests
{
diff --git a/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationDefaultCollationTest.cs b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationDefaultCollationTest.cs
new file mode 100644
index 0000000..35acd38
--- /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.NetCore.Console;
+using SQLite.CodeFirst.NetCore.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
index 6db4301..22b3efa 100644
--- a/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationTest.cs
+++ b/SQLite.CodeFirst.Test/IntegrationTests/SqlGenerationTest.cs
@@ -1,12 +1,11 @@
-using System;
-using System.Data.Common;
+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;
+using SQLite.CodeFirst.NetCore.Console;
+using SQLite.CodeFirst.NetCore.Console.Entity;
namespace SQLite.CodeFirst.Test.IntegrationTests
{
@@ -14,18 +13,19 @@ namespace SQLite.CodeFirst.Test.IntegrationTests
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 ""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 ""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 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"");
@@ -42,12 +42,13 @@ public class SqlGenerationTest
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"");";
+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()
{
diff --git a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
index caf835b..0228e1e 100644
--- a/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
+++ b/SQLite.CodeFirst.Test/SQLite.CodeFirst.Test.csproj
@@ -1,34 +1,24 @@
-
+
- net48
+ net10.0
SQLite.CodeFirst.Test
Contains the Unit Tests for the SQLite.CodeFirst Library.
1.0.0.0
true
..\Shared\SQLite.CodeFirst.StrongNameKey.snk
+
+ Exe
+ true
-
- full
-
-
- pdbonly
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
+
-
\ No newline at end of file
+
diff --git a/SQLite.CodeFirst.Test/TestSetup.cs b/SQLite.CodeFirst.Test/TestSetup.cs
new file mode 100644
index 0000000..e180699
--- /dev/null
+++ b/SQLite.CodeFirst.Test/TestSetup.cs
@@ -0,0 +1,23 @@
+using System.Data.Entity;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.NetCore.Console;
+
+namespace SQLite.CodeFirst.Test
+{
+ ///
+ /// Registers the SQLite Entity Framework 6 provider once for the whole test assembly.
+ /// On .NET (Core) there is no app.config based provider discovery, so the code based
+ /// from the demo project is applied before any test runs.
+ /// Setting it explicitly (instead of relying on assembly scanning) makes the registration
+ /// independent of which DbContext the test runner happens to touch first.
+ ///
+ [TestClass]
+ public static class TestSetup
+ {
+ [AssemblyInitialize]
+ public static void Initialize(TestContext context)
+ {
+ DbConfiguration.SetConfiguration(new Configuration());
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/DbInitializers/InitializerDefaultCollationTest.cs b/SQLite.CodeFirst.Test/UnitTests/DbInitializers/InitializerDefaultCollationTest.cs
new file mode 100644
index 0000000..256b323
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/DbInitializers/InitializerDefaultCollationTest.cs
@@ -0,0 +1,64 @@
+using System.Data.Entity;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.NetCore.Console;
+
+namespace SQLite.CodeFirst.Test.UnitTests.DbInitializers
+{
+ ///
+ /// Verifies that the public initializers forward an explicitly supplied default
+ /// to the base class, which is what feeds it into the SQL generation.
+ /// Without this the documented "default collation" feature is unreachable through the shipped initializers.
+ ///
+ [TestClass]
+ public class InitializerDefaultCollationTest
+ {
+ private static readonly Collation Collation = new Collation(CollationFunction.RTrim);
+
+ private static DbModelBuilder NewModelBuilder()
+ {
+ return new DbModelBuilder();
+ }
+
+ [TestMethod]
+ public void SqliteCreateDatabaseIfNotExists_ForwardsDefaultCollation()
+ {
+ var initializer = new SqliteCreateDatabaseIfNotExists(NewModelBuilder(), Collation);
+ Assert.AreSame(Collation, initializer.DefaultCollation);
+ }
+
+ [TestMethod]
+ public void SqliteCreateDatabaseIfNotExists_WithNullByteFlag_ForwardsDefaultCollation()
+ {
+ var initializer = new SqliteCreateDatabaseIfNotExists(NewModelBuilder(), true, Collation);
+ Assert.AreSame(Collation, initializer.DefaultCollation);
+ }
+
+ [TestMethod]
+ public void SqliteDropCreateDatabaseAlways_ForwardsDefaultCollation()
+ {
+ var initializer = new SqliteDropCreateDatabaseAlways(NewModelBuilder(), Collation);
+ Assert.AreSame(Collation, initializer.DefaultCollation);
+ }
+
+ [TestMethod]
+ public void SqliteDropCreateDatabaseWhenModelChanges_ForwardsDefaultCollation()
+ {
+ var initializer = new SqliteDropCreateDatabaseWhenModelChanges(NewModelBuilder(), Collation);
+ Assert.AreSame(Collation, initializer.DefaultCollation);
+ }
+
+ [TestMethod]
+ public void SqliteDropCreateDatabaseWhenModelChanges_WithHistoryType_ForwardsDefaultCollation()
+ {
+ var initializer = new SqliteDropCreateDatabaseWhenModelChanges(NewModelBuilder(), typeof(History), Collation);
+ Assert.AreSame(Collation, initializer.DefaultCollation);
+ }
+
+ [TestMethod]
+ public void Initializer_WithoutCollation_HasNullDefaultCollation()
+ {
+ var initializer = new SqliteDropCreateDatabaseAlways(NewModelBuilder());
+ Assert.IsNull(initializer.DefaultCollation);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs
index 8697d24..bc02127 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/CollateConstraintTest.cs
@@ -12,7 +12,7 @@ public void CreateStatement_StatementIsCorrect_NoConstraint()
var collationConstraint = new CollateConstraint();
collationConstraint.CollationFunction = CollationFunction.None;
string output = collationConstraint.CreateStatement();
- Assert.AreEqual(output, "");
+ Assert.AreEqual("", output);
}
[TestMethod]
@@ -21,7 +21,7 @@ public void CreateStatement_StatementIsCorrect_NoCase()
var collationConstraint = new CollateConstraint();
collationConstraint.CollationFunction = CollationFunction.NoCase;
string output = collationConstraint.CreateStatement();
- Assert.AreEqual(output, "COLLATE NOCASE");
+ Assert.AreEqual("COLLATE NOCASE", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
index eb6aa47..08def45 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/ColumnConstraintCollectionTest.cs
@@ -18,7 +18,7 @@ public void CreateStatementOneColumnConstraintTest()
columnConstraintMock.Object
});
string output = columnConstraintCollection.CreateStatement();
- Assert.AreEqual(output, "dummy1");
+ Assert.AreEqual("dummy1", output);
}
[TestMethod]
@@ -36,7 +36,7 @@ public void CreateStatementTwoColumnConstraintsTest()
columnConstraintMock2.Object
});
string output = columnConstraintCollection.CreateStatement();
- Assert.AreEqual(output, "dummy1 dummy2");
+ Assert.AreEqual("dummy1 dummy2", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs
index da2a20a..a8b01b1 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/DefaultValueConstraintTest.cs
@@ -12,7 +12,7 @@ public void CreateStatement_StatementIsCorrect_IntDefault()
var defaultValueConstraint = new DefaultValueConstraint();
defaultValueConstraint.DefaultValue = "0";
string output = defaultValueConstraint.CreateStatement();
- Assert.AreEqual(output, "DEFAULT (0)");
+ Assert.AreEqual("DEFAULT (0)", output);
}
[TestMethod]
@@ -21,7 +21,7 @@ public void CreateStatement_StatementIsCorrect_StringDefault()
var defaultValueConstraint = new DefaultValueConstraint();
defaultValueConstraint.DefaultValue = @"'Something'";
string output = defaultValueConstraint.CreateStatement();
- Assert.AreEqual(output, "DEFAULT ('Something')");
+ Assert.AreEqual("DEFAULT ('Something')", output);
}
[TestMethod]
@@ -30,7 +30,7 @@ public void CreateStatement_StatementIsCorrect_ExpressionDefault()
var defaultValueConstraint = new DefaultValueConstraint();
defaultValueConstraint.DefaultValue = @"datetime('now')";
string output = defaultValueConstraint.CreateStatement();
- Assert.AreEqual(output, "DEFAULT (datetime('now'))");
+ Assert.AreEqual("DEFAULT (datetime('now'))", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
index 1df077f..3436359 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/MaxLengthConstraint.cs
@@ -12,15 +12,14 @@ public void CreateStatementTest()
{
var maxLengthConstraint = new MaxLengthConstraint(12);
string output = maxLengthConstraint.CreateStatement();
- Assert.AreEqual(output, "(12)");
+ Assert.AreEqual("(12)", output);
}
[TestMethod]
- [ExpectedException(typeof(InvalidOperationException))]
public void CreateStatementInvalidParameterTest()
{
var maxLengthConstraint = new MaxLengthConstraint();
- maxLengthConstraint.CreateStatement();
+ Assert.ThrowsExactly(() => maxLengthConstraint.CreateStatement());
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
index 747fdbf..59ea347 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/NotNullConstraintTest.cs
@@ -11,7 +11,7 @@ public void CreateStatementTest()
{
var notNullConstraint = new NotNullConstraint();
string output = notNullConstraint.CreateStatement();
- Assert.AreEqual(output, "NOT NULL");
+ Assert.AreEqual("NOT NULL", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs
index 5d5bc6d..71a9aea 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/PrimaryKeyConstraintTest.cs
@@ -11,7 +11,7 @@ public void CreateStatement_StatementIsCorrect_WithAutoincrement()
{
var primaryKeyConstraint = new PrimaryKeyConstraint { Autoincrement = true };
string output = primaryKeyConstraint.CreateStatement();
- Assert.AreEqual(output, "PRIMARY KEY AUTOINCREMENT");
+ Assert.AreEqual("PRIMARY KEY AUTOINCREMENT", output);
}
[TestMethod]
@@ -19,7 +19,7 @@ public void CreateStatement_StatementIsCorrect()
{
var primaryKeyConstraint = new PrimaryKeyConstraint();
string output = primaryKeyConstraint.CreateStatement();
- Assert.AreEqual(output, "PRIMARY KEY");
+ Assert.AreEqual("PRIMARY KEY", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs
index d09e2c5..ece92e1 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnConstraint/UniqueConstraintTest.cs
@@ -12,7 +12,7 @@ public void CreateStatement_StatementIsCorrect_NoConstraint()
var uniqueConstraint = new UniqueConstraint();
uniqueConstraint.OnConflict = OnConflictAction.None;
string output = uniqueConstraint.CreateStatement();
- Assert.AreEqual(output, "UNIQUE");
+ Assert.AreEqual("UNIQUE", output);
}
[TestMethod]
@@ -21,7 +21,7 @@ public void CreateStatement_StatementIsCorrect_WithConstraint()
var uniqueConstraint = new UniqueConstraint();
uniqueConstraint.OnConflict = OnConflictAction.Rollback;
string output = uniqueConstraint.CreateStatement();
- Assert.AreEqual(output, "UNIQUE ON CONFLICT ROLLBACK");
+ Assert.AreEqual("UNIQUE ON CONFLICT ROLLBACK", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
index eccb3fb..d40913b 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementCollectionTest.cs
@@ -12,8 +12,8 @@ public void CreateStatementOneEntryTest()
var columnStatementCollection = new ColumnStatementCollection(new[] { CreateStatementMock("dummy1").Object });
string output = columnStatementCollection.CreateStatement();
- Assert.AreEqual(columnStatementCollection.Count, 1);
- Assert.AreEqual(output, "dummy1");
+ Assert.AreEqual(1, columnStatementCollection.Count);
+ Assert.AreEqual("dummy1", output);
}
[TestMethod]
@@ -26,8 +26,8 @@ public void CreateStatementTwoEntryTest()
});
string output = createIndexStatementCollection.CreateStatement();
- Assert.AreEqual(createIndexStatementCollection.Count, 2);
- Assert.AreEqual(output, "dummy1, dummy2");
+ Assert.AreEqual(2, createIndexStatementCollection.Count);
+ Assert.AreEqual("dummy1, dummy2", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
index 4d81d75..7643abc 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ColumnStatementTest.cs
@@ -16,12 +16,12 @@ 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");
+ Assert.AreEqual("[dummyColumnName] dummyType dummyColumnConstraint", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
index 33e4f39..888574e 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateDatabaseStatementTest.cs
@@ -16,8 +16,8 @@ public void CreateStatementWithOneKeyTest()
};
var createDatabaseStatement = new CreateDatabaseStatement(statements);
- Assert.AreEqual(createDatabaseStatement.Count, 1);
- Assert.AreEqual(createDatabaseStatement.CreateStatement(), "dummy1");
+ Assert.AreEqual(1, createDatabaseStatement.Count);
+ Assert.AreEqual("dummy1", createDatabaseStatement.CreateStatement());
}
[TestMethod]
@@ -30,8 +30,8 @@ public void CreateStatementWithTwoKeyTest()
};
var createDatabaseStatement = new CreateDatabaseStatement(statements);
- Assert.AreEqual(createDatabaseStatement.Count, 2);
- Assert.AreEqual(createDatabaseStatement.CreateStatement(), "dummy1\r\ndummy2");
+ Assert.AreEqual(2, createDatabaseStatement.Count);
+ Assert.AreEqual("dummy1\r\ndummy2", createDatabaseStatement.CreateStatement());
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
index 1082710..b90626c 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateIndexStatementCollectionTest.cs
@@ -15,8 +15,8 @@ public void CreateStatementOneEntryTest()
});
string output = createIndexStatementCollection.CreateStatement();
- Assert.AreEqual(createIndexStatementCollection.Count, 1);
- Assert.AreEqual(output, "dummy1");
+ Assert.AreEqual(1, createIndexStatementCollection.Count);
+ Assert.AreEqual("dummy1", output);
}
[TestMethod]
@@ -29,8 +29,8 @@ public void CreateStatementTwoEntryTest()
});
string output = createIndexStatementCollection.CreateStatement();
- Assert.AreEqual(createIndexStatementCollection.Count, 2);
- Assert.AreEqual(output, "dummy1\r\ndummy2");
+ Assert.AreEqual(2, createIndexStatementCollection.Count);
+ Assert.AreEqual("dummy1\r\ndummy2", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
index 31d3817..1d19219 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/CreateTableStatementTest.cs
@@ -16,7 +16,7 @@ public void CreateStatementTest()
};
string output = createTableStatement.CreateStatement();
- Assert.AreEqual(output, "CREATE TABLE dummyTable (dummyColumnDefinition);");
+ Assert.AreEqual("CREATE TABLE dummyTable (dummyColumnDefinition);", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
index 192a6b7..65dd2d7 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/ForeignKeyStatementTest.cs
@@ -19,7 +19,7 @@ public void CreateStatementOneForeignKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1)");
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1])", output);
}
[TestMethod]
@@ -34,7 +34,38 @@ public void CreateStatementOneForeignKeyCascadeDeleteTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1) ON DELETE CASCADE");
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1]) ON DELETE CASCADE", output);
+ }
+
+ [TestMethod]
+ public void CreateStatementOneForeignKeyCascadeUpdateTest()
+ {
+ var foreignKeyStatement = new ForeignKeyStatement
+ {
+ CascadeUpdate = true,
+ ForeignKey = new List { "dummyForeignKey1" },
+ ForeignPrimaryKey = new List { "dummForeignPrimaryKey1" },
+ ForeignTable = "dummyForeignTable"
+ };
+
+ string output = foreignKeyStatement.CreateStatement();
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1]) ON UPDATE CASCADE", output);
+ }
+
+ [TestMethod]
+ public void CreateStatementOneForeignKeyCascadeDeleteAndUpdateTest()
+ {
+ var foreignKeyStatement = new ForeignKeyStatement
+ {
+ CascadeDelete = true,
+ CascadeUpdate = true,
+ ForeignKey = new List { "dummyForeignKey1" },
+ ForeignPrimaryKey = new List { "dummForeignPrimaryKey1" },
+ ForeignTable = "dummyForeignTable"
+ };
+
+ string output = foreignKeyStatement.CreateStatement();
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1]) ON DELETE CASCADE ON UPDATE CASCADE", output);
}
[TestMethod]
@@ -49,7 +80,7 @@ public void CreateStatementTwoForeignKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1, dummyForeignKey2) REFERENCES dummyForeignTable(dummForeignPrimaryKey1)");
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1], [dummyForeignKey2]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1])", output);
}
[TestMethod]
@@ -64,7 +95,7 @@ public void CreateStatementTwoForeignKeyTwoPrimaryKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1, dummyForeignKey2) REFERENCES dummyForeignTable(dummForeignPrimaryKey1, dummForeignPrimaryKey2)");
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1], [dummyForeignKey2]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1], [dummForeignPrimaryKey2])", output);
}
[TestMethod]
@@ -79,7 +110,7 @@ public void CreateStatementOneForeignKeyTwoPrimaryKeyTest()
};
string output = foreignKeyStatement.CreateStatement();
- Assert.AreEqual(output, "FOREIGN KEY (dummyForeignKey1) REFERENCES dummyForeignTable(dummForeignPrimaryKey1, dummForeignPrimaryKey2)");
+ Assert.AreEqual("FOREIGN KEY ([dummyForeignKey1]) REFERENCES dummyForeignTable([dummForeignPrimaryKey1], [dummForeignPrimaryKey2])", output);
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs b/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
index 7ece059..b83d2c1 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Statement/PrimaryKeyStatementTest.cs
@@ -13,8 +13,8 @@ public void CreateStatementWithOneKeyTest()
const string keyMember1 = "keyMember1";
var primaryKeyStatement = new CompositePrimaryKeyStatement(new List { keyMember1 });
- Assert.AreEqual(primaryKeyStatement.Count, 1);
- Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY(keyMember1)");
+ Assert.AreEqual(1, primaryKeyStatement.Count);
+ Assert.AreEqual("PRIMARY KEY([keyMember1])", primaryKeyStatement.CreateStatement());
}
[TestMethod]
@@ -24,8 +24,8 @@ public void CreateStatementWithTwoKeyTest()
const string keyMember2 = "keyMember2";
var primaryKeyStatement = new CompositePrimaryKeyStatement(new List { keyMember1, keyMember2 });
- Assert.AreEqual(primaryKeyStatement.Count, 2);
- Assert.AreEqual(primaryKeyStatement.CreateStatement(), "PRIMARY KEY(keyMember1, keyMember2)");
+ Assert.AreEqual(2, primaryKeyStatement.Count);
+ Assert.AreEqual("PRIMARY KEY([keyMember1], [keyMember2])", primaryKeyStatement.CreateStatement());
}
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs
index 998f5c8..d6e6238 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/ConnectionStringParserTest.cs
@@ -106,5 +106,33 @@ public void GetDataSource_ReturnsCorrectDataSource_RemovesQuotationMark()
// Assert
Assert.AreEqual(@".\db\footballDb\footballDb.sqlite", result);
}
+
+ [TestMethod]
+ public void GetDataSource_ReturnsCorrectDataSource_WhenValueContainsEqualsSign()
+ {
+ // Arrange
+ string connectionString = @"data source=.\db\foo=bar\footballDb.sqlite;foreign keys=true";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@".\db\foo=bar\footballDb.sqlite", result);
+ }
+
+ [TestMethod]
+ public void GetDataSource_ReturnsLastValue_WhenKeyIsRepeated()
+ {
+ // Arrange
+ string connectionString = @"data source=first.sqlite;data source=second.sqlite";
+
+
+ // Act
+ string result = ConnectionStringParser.GetDataSource(connectionString);
+
+ // Assert
+ Assert.AreEqual(@"second.sqlite", result);
+ }
}
}
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs
index 61fcfb2..590c735 100644
--- a/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryEntityTypeValidatorTest.cs
@@ -8,17 +8,17 @@ namespace SQLite.CodeFirst.Test.UnitTests.Utility
public class HistoryEntityTypeValidatorTest
{
[TestMethod]
- [ExpectedException(typeof(InvalidOperationException))]
public void EnsureValidTypeNotIHistory()
{
- HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType1));
+ Assert.ThrowsExactly(
+ () => HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType1)));
}
[TestMethod]
- [ExpectedException(typeof(InvalidOperationException))]
public void EnsureValidTypeNoParamLessCtorTest()
{
- HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType2));
+ Assert.ThrowsExactly(
+ () => HistoryEntityTypeValidator.EnsureValidType(typeof(InvalidFakeHistoryType2)));
}
[TestMethod]
diff --git a/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryRecordSelectorTest.cs b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryRecordSelectorTest.cs
new file mode 100644
index 0000000..b53a240
--- /dev/null
+++ b/SQLite.CodeFirst.Test/UnitTests/Utility/HistoryRecordSelectorTest.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using SQLite.CodeFirst.Utility;
+
+namespace SQLite.CodeFirst.Test.UnitTests.Utility
+{
+ [TestClass]
+ public class HistoryRecordSelectorTest
+ {
+ private static IHistory Record(string context)
+ {
+ return new History { Context = context, Hash = "hash-" + context };
+ }
+
+ [TestMethod]
+ public void SelectForContext_ReturnsRecordForGivenContext()
+ {
+ var records = new List { Record("ContextA"), Record("ContextB") };
+
+ IHistory result = HistoryRecordSelector.SelectForContext(records, "ContextB");
+
+ Assert.IsNotNull(result);
+ Assert.AreEqual("ContextB", result.Context);
+ }
+
+ [TestMethod]
+ public void SelectForContext_ReturnsNull_WhenNoRecordMatches()
+ {
+ var records = new List { Record("ContextA") };
+
+ IHistory result = HistoryRecordSelector.SelectForContext(records, "ContextB");
+
+ Assert.IsNull(result);
+ }
+
+ [TestMethod]
+ public void SelectForContext_ReturnsNull_WhenEmpty()
+ {
+ IHistory result = HistoryRecordSelector.SelectForContext(new List(), "ContextA");
+
+ Assert.IsNull(result);
+ }
+
+ [TestMethod]
+ public void SelectForContext_Throws_WhenMultipleRecordsShareContext()
+ {
+ // One record per context is an invariant maintained by SaveHistory. If it is ever
+ // violated, surfacing it is better than silently picking an arbitrary record.
+ var records = new List { Record("ContextA"), Record("ContextA") };
+
+ Assert.ThrowsExactly(
+ () => HistoryRecordSelector.SelectForContext(records, "ContextA"));
+ }
+ }
+}
diff --git a/SQLite.CodeFirst.Test/app.config b/SQLite.CodeFirst.Test/app.config
deleted file mode 100644
index 2f401ce..0000000
--- a/SQLite.CodeFirst.Test/app.config
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/SQLite.CodeFirst.Test/packages.lock.json b/SQLite.CodeFirst.Test/packages.lock.json
new file mode 100644
index 0000000..2dccd5e
--- /dev/null
+++ b/SQLite.CodeFirst.Test/packages.lock.json
@@ -0,0 +1,256 @@
+{
+ "version": 1,
+ "dependencies": {
+ "net10.0": {
+ "Microsoft.Testing.Extensions.TrxReport": {
+ "type": "Direct",
+ "requested": "[2.2.3, )",
+ "resolved": "2.2.3",
+ "contentHash": "9Hot3ty5ZVWHrW40k2NPfD0dCaPwIxj7j7VjujNYwpYkYw9AdbejPHjGNkL/gvUWorauJf5IkeDoUeIbS7LuUg==",
+ "dependencies": {
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3",
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Moq": {
+ "type": "Direct",
+ "requested": "[4.20.72, )",
+ "resolved": "4.20.72",
+ "contentHash": "EA55cjyNn8eTNWrgrdZJH5QLFp2L43oxl1tlkoYUKIE9pRwL784OWiTXeCV5ApS+AMYEAlt7Fo03A2XfouvHmQ==",
+ "dependencies": {
+ "Castle.Core": "5.1.1"
+ }
+ },
+ "MSTest.TestAdapter": {
+ "type": "Direct",
+ "requested": "[4.2.3, )",
+ "resolved": "4.2.3",
+ "contentHash": "oVV/luk0bBghnVvLaw8MwFlD7It0Cx9P2nKobeqIafmTQqFFWY69Wo801dxjeNaLzO/o9WQ84WSK84gXJuubhg==",
+ "dependencies": {
+ "MSTest.TestFramework": "4.2.3",
+ "Microsoft.Testing.Extensions.VSTestBridge": "2.2.3",
+ "Microsoft.Testing.Platform.MSBuild": "2.2.3"
+ }
+ },
+ "MSTest.TestFramework": {
+ "type": "Direct",
+ "requested": "[4.2.3, )",
+ "resolved": "4.2.3",
+ "contentHash": "9zzij59YLh+tf+FRLNqhzHjmdspR91bol+jdQxLlxxTGMAML6LDbvuyXKMGdcrE84+QpKkk6KjTVBC5PBGrDeA==",
+ "dependencies": {
+ "MSTest.Analyzers": "4.2.3"
+ }
+ },
+ "Castle.Core": {
+ "type": "Transitive",
+ "resolved": "5.1.1",
+ "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==",
+ "dependencies": {
+ "System.Diagnostics.EventLog": "6.0.0"
+ }
+ },
+ "EntityFramework": {
+ "type": "Transitive",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA==",
+ "dependencies": {
+ "System.CodeDom": "6.0.0",
+ "System.ComponentModel.Annotations": "5.0.0",
+ "System.Configuration.ConfigurationManager": "6.0.1",
+ "System.Data.SqlClient": "4.8.6"
+ }
+ },
+ "Microsoft.ApplicationInsights": {
+ "type": "Transitive",
+ "resolved": "2.23.0",
+ "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw=="
+ },
+ "Microsoft.Testing.Extensions.Telemetry": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "mLdW+JOR3kXYGTdgR/qc/UZBA0r+eCR2k6bUxTcuDj5w9WdIQ7Lol5MBUU7YOSGd9bs9bvhSYWAptgz0YtQqCA==",
+ "dependencies": {
+ "Microsoft.ApplicationInsights": "2.23.0",
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "hntvxJEkmUAx6C2xXc/PO38DqEQl4rimzOgSvTR1hAMruMid7R4RcXOrzzF33J66gKaN7jRaQ0TMW/nNfaV9jw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.Testing.Extensions.VSTestBridge": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "7WlJISO8QKUK+d+WhgnANwy4ACwUrvICnviY/mthPwjZ2gVeDaSUAeBnMy2cxfzZgm8VATtGUDbYzUxsgV2CyQ==",
+ "dependencies": {
+ "Microsoft.TestPlatform.ObjectModel": "18.3.0",
+ "Microsoft.Testing.Extensions.Telemetry": "2.2.3",
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3",
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "LhM1/Qoi8Ams5QcD4r3f09CSOono9iQr3NEJQItFtyzWB55nWTgEOsVqXqMWWWIwk3nkPqc+XfnlJmp8xUI5fg=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "2.2.3",
+ "contentHash": "Q22jJYJLx4srTinsAuoCskqmzjrBJC8YeGJMHHIcrf1dQeHoEZ7wsqDzTlENkMoke2qfufF7U+9u58nlZunH/Q==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "2.2.3"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "18.3.0",
+ "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ=="
+ },
+ "Microsoft.Win32.SystemEvents": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
+ },
+ "MSTest.Analyzers": {
+ "type": "Transitive",
+ "resolved": "4.2.3",
+ "contentHash": "dxOZt8/LWuiox7rugInJoIa5Mmu3pBmXdfaoZOx/mxx8+sUFFpjBXPlWXQXGeWzpkVPNC3x1Jf7rt2h2Zjyvvg=="
+ },
+ "runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==",
+ "dependencies": {
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
+ }
+ },
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
+ },
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
+ },
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
+ },
+ "Stub.System.Data.SQLite.Core.NetStandard": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "dI7ngiCNgdm+n00nQvFTa+LbHvE9MIQXwMSLRzJI/KAJ7G1WmCachsvfE1CD6xvb3OXJvYYEfv3+S/LHyhN0Rg=="
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.ComponentModel.Annotations": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
+ },
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "6.0.1",
+ "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "6.0.0",
+ "System.Security.Permissions": "6.0.0"
+ }
+ },
+ "System.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "4.8.6",
+ "contentHash": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==",
+ "dependencies": {
+ "runtime.native.System.Data.SqlClient.sni": "4.7.0"
+ }
+ },
+ "System.Data.SQLite": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "JSOJpnBf9goMnxGTJFGCmm6AffxgtpuXNXV5YvWO8UNC2zwd12qkUe5lAbnY+2ohIkIukgIjbvR1RA/sWILv3w==",
+ "dependencies": {
+ "System.Data.SQLite.Core": "[1.0.119]",
+ "System.Data.SQLite.EF6": "[1.0.119]"
+ }
+ },
+ "System.Data.SQLite.Core": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "bhQB8HVtRA+OOYw8UTD1F1kU+nGJ0/OZvH1JmlVUI4bGvgVEWeX1NcHjA765NvUoRVuCPlt8PrEpZ1thSsk1jg==",
+ "dependencies": {
+ "Stub.System.Data.SQLite.Core.NetStandard": "[1.0.119]"
+ }
+ },
+ "System.Data.SQLite.EF6": {
+ "type": "Transitive",
+ "resolved": "1.0.119",
+ "contentHash": "BwwgCSeA80gsxdXtU7IQEBrN9kQXWQrD11hNYOJZbXBBI1C4r7hA4QhBAalO1nzijXikthGRUADIEMI3nlucLA==",
+ "dependencies": {
+ "EntityFramework": "6.4.4"
+ }
+ },
+ "System.Diagnostics.EventLog": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "lcyUiXTsETK2ALsZrX+nWuHSIQeazhqPphLfaRxzdGaG93+0kELqpgEHtwWOlQe7+jSFnKwaCAgL4kjeZCQJnw=="
+ },
+ "System.Drawing.Common": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
+ "dependencies": {
+ "Microsoft.Win32.SystemEvents": "6.0.0"
+ }
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
+ "dependencies": {
+ "System.Windows.Extensions": "6.0.0"
+ }
+ },
+ "System.Windows.Extensions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
+ "dependencies": {
+ "System.Drawing.Common": "6.0.0"
+ }
+ },
+ "sqlite.codefirst": {
+ "type": "Project",
+ "dependencies": {
+ "EntityFramework": "[6.5.2, )"
+ }
+ },
+ "sqlite.codefirst.netcore.console": {
+ "type": "Project",
+ "dependencies": {
+ "SQLite.CodeFirst": "[1.0.0, )",
+ "System.Data.SQLite": "[1.0.119, )",
+ "System.Data.SQLite.EF6": "[1.0.119, )"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/SQLite.CodeFirst.sln b/SQLite.CodeFirst.sln
deleted file mode 100644
index c1ff1b3..0000000
--- a/SQLite.CodeFirst.sln
+++ /dev/null
@@ -1,48 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.28010.2050
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLite.CodeFirst", "SQLite.CodeFirst\SQLite.CodeFirst.csproj", "{50A32FE4-0E13-4213-A373-72523CDF34D9}"
-EndProject
-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
- ci_appveyor.yml = ci_appveyor.yml
- release_appveyor.yml = release_appveyor.yml
- EndProjectSection
-EndProject
-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}") = "Shared", "Shared", "{1F6D4E0E-5D07-4889-961A-A6AFC0ECD7C7}"
- ProjectSection(SolutionItems) = preProject
- Shared\SQLite.CodeFirst.ruleset = Shared\SQLite.CodeFirst.ruleset
- Shared\SQLite.CodeFirst.StrongNameKey.snk = Shared\SQLite.CodeFirst.StrongNameKey.snk
- EndProjectSection
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {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}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {E542F38D-852E-489D-81C2-BF333503E10F}.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.slnx b/SQLite.CodeFirst.slnx
new file mode 100644
index 0000000..894535d
--- /dev/null
+++ b/SQLite.CodeFirst.slnx
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
index 93043c2..3fba1bb 100644
--- a/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/ColumnStatementCollectionBuilder.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
using SQLite.CodeFirst.Extensions;
@@ -11,11 +12,13 @@ internal class ColumnStatementCollectionBuilder : IStatementBuilder properties;
private readonly IEnumerable keyMembers;
+ private readonly Collation defaultCollation;
- public ColumnStatementCollectionBuilder(IEnumerable properties, IEnumerable keyMembers)
+ public ColumnStatementCollectionBuilder(IEnumerable properties, IEnumerable keyMembers, Collation defaultCollation)
{
this.properties = properties;
this.keyMembers = keyMembers;
+ this.defaultCollation = defaultCollation;
}
public ColumnStatementCollection BuildStatement()
@@ -39,7 +42,7 @@ private IEnumerable CreateColumnStatements()
AdjustDatatypeForAutogenerationIfNecessary(property, columnStatement);
AddNullConstraintIfNecessary(property, columnStatement);
AddUniqueConstraintIfNecessary(property, columnStatement);
- AddCollationConstraintIfNecessary(property, columnStatement);
+ AddCollationConstraintIfNecessary(property, columnStatement, defaultCollation);
AddPrimaryKeyConstraintAndAdjustTypeIfNecessary(property, columnStatement);
AddDefaultValueConstraintIfNecessary(property, columnStatement);
@@ -73,12 +76,25 @@ private static void AddNullConstraintIfNecessary(EdmProperty property, ColumnSta
}
}
- private static void AddCollationConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement)
+ private static void AddCollationConstraintIfNecessary(EdmProperty property, ColumnStatement columnStatement, Collation defaultCollation)
{
- var value = property.GetCustomAnnotation();
- if (value != null)
+ 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)
{
- columnStatement.ColumnConstraints.Add(new CollateConstraint { CollationFunction = value.Collation, CustomCollationFunction = value.Function });
+ // 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);
}
}
diff --git a/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
index 65862a2..ea87cca 100644
--- a/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/CreateDatabaseStatementBuilder.cs
@@ -9,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()
@@ -30,7 +32,7 @@ private IEnumerable GetCreateTableStatements()
foreach (var entitySet in edmModel.Container.EntitySets)
{
- var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeContainer);
+ var tableStatementBuilder = new CreateTableStatementBuilder(entitySet, associationTypeContainer, defaultCollation);
yield return tableStatementBuilder.BuildStatement();
}
}
diff --git a/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs
index b86f701..ac3198b 100644
--- a/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/CreateTableStatementBuilder.cs
@@ -12,11 +12,13 @@ internal class CreateTableStatementBuilder : IStatementBuilder();
diff --git a/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs b/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
index e5fd81a..0dd595c 100644
--- a/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
+++ b/SQLite.CodeFirst/Internal/Builder/ForeignKeyStatementBuilder.cs
@@ -29,7 +29,8 @@ private IEnumerable GetForeignKeyStatements()
ForeignKey = associationType.ForeignKey,
ForeignTable = associationType.FromTableName,
ForeignPrimaryKey = associationType.ForeignPrimaryKey,
- CascadeDelete = associationType.CascadeDelete
+ CascadeDelete = associationType.CascadeDelete,
+ CascadeUpdate = associationType.CascadeUpdate
};
}
}
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/Internal/Convention/SqliteForeignKeyIndexConvention.cs b/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
index e10205e..e5d24ac 100644
--- a/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
+++ b/SQLite.CodeFirst/Internal/Convention/SqliteForeignKeyIndexConvention.cs
@@ -74,16 +74,16 @@ public virtual void Apply(AssociationType item, DbModel model)
private static IndexAnnotation CreateIndexAnnotation(string tableName, string propertyName, int count)
{
- 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(CultureInfo.InvariantCulture, "{0}_{1}", indexName, count);
- }
+ // If there are two indices on the same property, the count is appended.
+ // In SQLite an index name must be globally unique.
+ // To be honest, it should never happen. But because it is possible via the API, it is covered.
+ // The suffix is folded in before escaping so it stays inside the quoted identifier;
+ // IndexNameCreator.CreateName returns an already-escaped name.
+ string uniquePropertyName = count > 0
+ ? String.Format(CultureInfo.InvariantCulture, "{0}_{1}", propertyName, count)
+ : propertyName;
+ var indexName = IndexNameCreator.CreateName(tableName, uniquePropertyName);
var indexAttribute = new IndexAttribute(indexName);
return new IndexAnnotation(indexAttribute);
}
diff --git a/SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs b/SQLite.CodeFirst/Internal/Statement/ColumnStatement.cs
index bf4f91d..d55b091 100644
--- a/SQLite.CodeFirst/Internal/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/Internal/Statement/CompositePrimaryKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
index f2d0374..196f305 100644
--- a/SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
+++ b/SQLite.CodeFirst/Internal/Statement/CompositePrimaryKeyStatement.cs
@@ -1,13 +1,14 @@
-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 CompositePrimaryKeyStatement : Collection, IStatement
{
private const string Template = "PRIMARY KEY({primary-keys})";
- private const string PrimaryKeyColumnNameSeperator = ", ";
public CompositePrimaryKeyStatement(IEnumerable keyMembers)
{
@@ -19,7 +20,7 @@ public CompositePrimaryKeyStatement(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/Internal/Statement/ForeignKeyStatement.cs b/SQLite.CodeFirst/Internal/Statement/ForeignKeyStatement.cs
index 19b61b4..6b862a7 100644
--- a/SQLite.CodeFirst/Internal/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
@@ -7,24 +9,28 @@ internal class ForeignKeyStatement : IStatement
{
private const string Template = "FOREIGN KEY ({foreign-key}) REFERENCES {referenced-table}({referenced-id})";
private const string CascadeDeleteStatement = "ON DELETE CASCADE";
+ private const string CascadeUpdateStatement = "ON UPDATE CASCADE";
public IEnumerable ForeignKey { get; set; }
public string ForeignTable { get; set; }
public IEnumerable ForeignPrimaryKey { get; set; }
public bool CascadeDelete { get; set; }
+ public bool CascadeUpdate { get; set; }
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);
}
+ if (CascadeUpdate)
+ {
+ sb.Append(" " + CascadeUpdateStatement);
+ }
return sb.ToString();
}
diff --git a/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs b/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs
index e86d20f..c22d1a9 100644
--- a/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs
+++ b/SQLite.CodeFirst/Internal/Utility/AssociationTypeContainer.cs
@@ -10,7 +10,9 @@ internal class AssociationTypeContainer
public AssociationTypeContainer(IEnumerable associationTypes, EntityContainer container)
{
- sqliteAssociationTypes = associationTypes.Select(associationType => new SqliteAssociationType(associationType, container));
+ // Materialize once. GetAssociationTypes is called per entity set, so a deferred query
+ // would rebuild every SqliteAssociationType (and its entity-set lookups) on each call.
+ sqliteAssociationTypes = associationTypes.Select(associationType => new SqliteAssociationType(associationType, container)).ToList();
}
public IEnumerable GetAssociationTypes(string entitySetName)
diff --git a/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
index 02c2c21..93592c6 100644
--- a/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
+++ b/SQLite.CodeFirst/Internal/Utility/ConnectionStringParser.cs
@@ -21,7 +21,7 @@ public static string GetDataSource(string connectionString)
IDictionary strings = ParseConnectionString(connectionString);
if (strings.ContainsKey(DataSourceToken))
{
- var path = ExpandDataDirectory(ParseConnectionString(connectionString)[DataSourceToken]);
+ var path = ExpandDataDirectory(strings[DataSourceToken]);
return path.Trim('"');
}
@@ -42,10 +42,13 @@ private static IDictionary ParseConnectionString(string connecti
IDictionary keyValuePairDictionary = new Dictionary();
foreach (var keyValuePair in keyValuePairs)
{
- string[] keyValue = keyValuePair.Split(KeyValueSeperator);
+ // Split on the first '=' only so a value that itself contains '=' is preserved.
+ // Input: "data source=C:\a=b.sqlite" -> key="data source", value="C:\a=b.sqlite"
+ string[] keyValue = keyValuePair.Split(new[] { KeyValueSeperator }, 2, StringSplitOptions.None);
if (keyValue.Length >= 2)
{
- keyValuePairDictionary.Add(keyValue[KeyPosition].Trim().ToLower(CultureInfo.InvariantCulture), keyValue[ValuePosition]);
+ // Indexer assignment (last value wins) so a repeated key does not throw.
+ keyValuePairDictionary[keyValue[KeyPosition].Trim().ToLower(CultureInfo.InvariantCulture)] = keyValue[ValuePosition];
}
}
diff --git a/SQLite.CodeFirst/Internal/Utility/HashCreator.cs b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs
index bd16c54..f8732a6 100644
--- a/SQLite.CodeFirst/Internal/Utility/HashCreator.cs
+++ b/SQLite.CodeFirst/Internal/Utility/HashCreator.cs
@@ -9,7 +9,7 @@ internal static class HashCreator
public static string CreateHash(string data)
{
byte[] dataBytes = Encoding.ASCII.GetBytes(data);
- using (SHA512 sha512 = new SHA512Managed())
+ using (SHA512 sha512 = SHA512.Create())
{
byte[] hashBytes = sha512.ComputeHash(dataBytes);
string hash = Convert.ToBase64String(hashBytes);
diff --git a/SQLite.CodeFirst/Internal/Utility/HistoryRecordSelector.cs b/SQLite.CodeFirst/Internal/Utility/HistoryRecordSelector.cs
new file mode 100644
index 0000000..9621370
--- /dev/null
+++ b/SQLite.CodeFirst/Internal/Utility/HistoryRecordSelector.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SQLite.CodeFirst.Utility
+{
+ ///
+ /// Selects the history record that belongs to a specific context from the history table.
+ /// A database can be shared by multiple contexts, so the lookup must be scoped by the context
+ /// key. Selecting without that scope would return more than one record on a shared database and
+ /// make the underlying throw.
+ ///
+ internal static class HistoryRecordSelector
+ {
+ public static IHistory SelectForContext(IEnumerable records, string contextKey)
+ {
+ return records.SingleOrDefault(record => record.Context == contextKey);
+ }
+ }
+}
diff --git a/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs b/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs
index 6ab4f49..7532614 100644
--- a/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs
+++ b/SQLite.CodeFirst/Internal/Utility/SqliteAssociationType.cs
@@ -2,6 +2,7 @@
using System.Data.Entity.Core.Metadata.Edm;
using System.Linq;
using SQLite.CodeFirst.Builder.NameCreators;
+using SQLite.CodeFirst.Extensions;
namespace SQLite.CodeFirst.Utility
{
@@ -30,6 +31,13 @@ public SqliteAssociationType(AssociationType associationType, EntityContainer co
ForeignKey = associationType.Constraint.ToProperties.Select(x => x.Name);
ForeignPrimaryKey = associationType.Constraint.FromProperties.Select(x => x.Name);
CascadeDelete = associationType.Constraint.FromRole.DeleteBehavior == OperationAction.Cascade;
+
+ // EF has no notion of update-cascade, so it is opt-in via the CascadeOnUpdateAttribute placed on the
+ // dependent foreign key property. The attribute is registered as a column annotation and therefore
+ // available on the store model foreign key columns (the constraint's ToProperties).
+ CascadeUpdate = associationType.Constraint.ToProperties
+ .Select(property => property.GetCustomAnnotation())
+ .Any(attribute => attribute != null && attribute.CanCascade);
}
private static bool IsSelfReferencing(AssociationType associationType)
@@ -47,5 +55,6 @@ private static bool IsSelfReferencing(AssociationType associationType)
public string ToTableName { get; }
public IEnumerable ForeignPrimaryKey { get; }
public bool CascadeDelete { get; }
+ public bool CascadeUpdate { get; }
}
}
\ No newline at end of file
diff --git a/SQLite.CodeFirst/Public/Attributes/CascadeOnUpdateAttribute.cs b/SQLite.CodeFirst/Public/Attributes/CascadeOnUpdateAttribute.cs
new file mode 100644
index 0000000..5870ccf
--- /dev/null
+++ b/SQLite.CodeFirst/Public/Attributes/CascadeOnUpdateAttribute.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace SQLite.CodeFirst
+{
+ ///
+ /// Adds 'ON UPDATE CASCADE' to the foreign key constraint of the relationship the decorated property belongs to.
+ /// Entity Framework has no concept of update-cascade (it assumes primary keys are immutable), so neither the
+ /// nor the fluent API can express it.
+ /// This opt-in attribute is the only way to emit the keyword.
+ /// Place it on the dependent foreign key property (e.g. the 'TeamId' property), not on the navigation property (e.g. 'Team').
+ /// Requires an explicit foreign key property; relationships with a shadow foreign key column cannot be decorated.
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class CascadeOnUpdateAttribute : Attribute
+ {
+ public CascadeOnUpdateAttribute()
+ {
+ CanCascade = true;
+ }
+
+ public CascadeOnUpdateAttribute(bool canCascade)
+ {
+ CanCascade = canCascade;
+ }
+
+ public bool CanCascade { get; }
+ }
+}
diff --git a/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs b/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs
index 2dfc272..636965a 100644
--- a/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs
+++ b/SQLite.CodeFirst/Public/Attributes/CollateAttribute.cs
@@ -12,39 +12,19 @@ public sealed class CollateAttribute : Attribute
{
public CollateAttribute()
{
- Collation = CollationFunction.None;
+ Collation = new Collation();
}
- public CollateAttribute(CollationFunction collation)
+ public CollateAttribute(CollationFunction function)
{
- if (collation == CollationFunction.Custom)
- {
- throw new ArgumentException("If the collation is set to CollationFunction.Custom a function must be specified.", nameof(collation));
- }
-
- Collation = collation;
+ Collation = new Collation(function);
}
- public CollateAttribute(CollationFunction collation, string function)
- {
- if (collation != CollationFunction.Custom && !string.IsNullOrEmpty(function))
- {
- throw new ArgumentException("If the collation is not set to CollationFunction.Custom a function must not be specified.", nameof(function));
- }
-
- if (collation == CollationFunction.Custom && string.IsNullOrEmpty(function))
- {
- throw new ArgumentException("If the collation is set to CollationFunction.Custom a function must be specified.", nameof(function));
- }
- Collation = collation;
- Function = function;
+ public CollateAttribute(CollationFunction function, string customFunction)
+ {
+ Collation = new Collation(function, customFunction);
}
- public CollationFunction Collation { get; }
-
- ///
- /// The name of the custom collating function to use (CollationFunction.Custom).
- ///
- public string Function { get; }
+ public Collation Collation { get; }
}
}
\ 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/Attributes/CollationFunction.cs b/SQLite.CodeFirst/Public/CollationFunction.cs
similarity index 95%
rename from SQLite.CodeFirst/Public/Attributes/CollationFunction.cs
rename to SQLite.CodeFirst/Public/CollationFunction.cs
index 1bb7f30..6bd054d 100644
--- a/SQLite.CodeFirst/Public/Attributes/CollationFunction.cs
+++ b/SQLite.CodeFirst/Public/CollationFunction.cs
@@ -2,7 +2,7 @@
{
///
/// The collation function to use for this column.
- /// Is used together with the .
+ /// Is used together with the , and when setting a default collation for the database.
///
public enum CollationFunction
{
diff --git a/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
index c572be6..6bfd7dd 100644
--- a/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteCreateDatabaseIfNotExists.cs
@@ -19,7 +19,16 @@ public class SqliteCreateDatabaseIfNotExists : SqliteInitializerBase
/// The model builder.
public SqliteCreateDatabaseIfNotExists(DbModelBuilder modelBuilder)
- : this(modelBuilder, false)
+ : this(modelBuilder, false, null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// The default collation applied to all string columns. Explicit s take precedence.
+ public SqliteCreateDatabaseIfNotExists(DbModelBuilder modelBuilder, Collation defaultCollation)
+ : this(modelBuilder, false, defaultCollation)
{ }
///
@@ -28,7 +37,17 @@ public SqliteCreateDatabaseIfNotExists(DbModelBuilder modelBuilder)
/// The model builder.
/// if set to true a null byte database file is treated like the database does not exist.
public SqliteCreateDatabaseIfNotExists(DbModelBuilder modelBuilder, bool nullByteFileMeansNotExisting)
- : base(modelBuilder)
+ : this(modelBuilder, nullByteFileMeansNotExisting, null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// if set to true a null byte database file is treated like the database does not exist.
+ /// The default collation applied to all string columns. Explicit s take precedence.
+ public SqliteCreateDatabaseIfNotExists(DbModelBuilder modelBuilder, bool nullByteFileMeansNotExisting, Collation defaultCollation)
+ : base(modelBuilder, defaultCollation)
{
this.nullByteFileMeansNotExisting = nullByteFileMeansNotExisting;
}
diff --git a/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
index 3154835..fb73aee 100644
--- a/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseAlways.cs
@@ -17,7 +17,16 @@ public class SqliteDropCreateDatabaseAlways : SqliteInitializerBase
/// The model builder.
public SqliteDropCreateDatabaseAlways(DbModelBuilder modelBuilder)
- : base(modelBuilder)
+ : this(modelBuilder, null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// The default collation applied to all string columns. Explicit s take precedence.
+ public SqliteDropCreateDatabaseAlways(DbModelBuilder modelBuilder, Collation defaultCollation)
+ : base(modelBuilder, defaultCollation)
{ }
///
diff --git a/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs
index f47feea..dbe050c 100644
--- a/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteDropCreateDatabaseWhenModelChanges.cs
@@ -34,11 +34,17 @@ public class SqliteDropCreateDatabaseWhenModelChanges : SqliteInitiali
///
/// The model builder.
public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder)
- : base(modelBuilder)
- {
- historyEntityType = typeof(History);
- ConfigureHistoryEntity();
- }
+ : this(modelBuilder, typeof(History), null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// The default collation applied to all string columns. Explicit s take precedence.
+ public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder, Collation defaultCollation)
+ : this(modelBuilder, typeof(History), defaultCollation)
+ { }
///
/// Initializes a new instance of the class.
@@ -46,7 +52,17 @@ public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder)
/// The model builder.
/// Type of the history entity (must implement and provide an parameterless constructor).
public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder, Type historyEntityType)
- : base(modelBuilder)
+ : this(modelBuilder, historyEntityType, null)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The model builder.
+ /// Type of the history entity (must implement and provide an parameterless constructor).
+ /// The default collation applied to all string columns. Explicit s take precedence.
+ public SqliteDropCreateDatabaseWhenModelChanges(DbModelBuilder modelBuilder, Type historyEntityType, Collation defaultCollation)
+ : base(modelBuilder, defaultCollation)
{
this.historyEntityType = historyEntityType;
ConfigureHistoryEntity();
@@ -61,16 +77,16 @@ protected void ConfigureHistoryEntity()
///
/// Initialize the database for the given context.
- /// Generates the SQLite-DDL from the model and executs it against the database.
+ /// 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 databseFilePath = GetDatabasePathFromContext(context);
+ string databaseFilePath = GetDatabasePathFromContext(context);
- bool dbExists = InMemoryAwareFile.Exists(databseFilePath);
+ bool dbExists = InMemoryAwareFile.Exists(databaseFilePath);
if (dbExists)
{
if (IsSameModel(context))
@@ -78,10 +94,11 @@ public override void InitializeDatabase(TContext context)
return;
}
- FileAttributes? attributes = InMemoryAwareFile.GetFileAttributes(databseFilePath);
- DeleteDatabase(context, databseFilePath);
+ FileAttributes? attributes = InMemoryAwareFile.GetFileAttributes(databaseFilePath);
+ CloseDatabase(context);
+ DeleteDatabase(context, databaseFilePath);
base.InitializeDatabase(context);
- InMemoryAwareFile.SetFileAttributes(databseFilePath, attributes);
+ InMemoryAwareFile.SetFileAttributes(databaseFilePath, attributes);
SaveHistory(context);
}
else
@@ -91,13 +108,22 @@ public override void InitializeDatabase(TContext 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 DeleteDatabase(TContext context, string databseFilePath)
+ private static void CloseDatabase(TContext context)
{
context.Database.Connection.Close();
GC.Collect();
GC.WaitForPendingFinalizers();
- InMemoryAwareFile.Delete(databseFilePath);
}
private void SaveHistory(TContext context)
@@ -149,7 +175,11 @@ private IHistory GetHistoryRecord(TContext context)
// in order to be supported by .NET 4.0.
DbQuery dbQuery = context.Set(historyEntityType).AsNoTracking();
IEnumerable records = Enumerable.Cast(dbQuery);
- return records.SingleOrDefault();
+
+ // A database can be shared by several contexts, so the record must be looked up by the
+ // context key that SaveHistory writes. An unscoped lookup would match every context's
+ // record and throw on a shared history table.
+ return HistoryRecordSelector.SelectForContext(records, context.GetType().FullName);
}
private string GetHashFromModel(DbConnection connection)
@@ -162,7 +192,7 @@ private string GetHashFromModel(DbConnection connection)
private string GetSqlFromModel(DbConnection connection)
{
var model = ModelBuilder.Build(connection);
- var sqliteSqlGenerator = new SqliteSqlGenerator();
+ 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
index a84cdf8..4e20b33 100644
--- a/SQLite.CodeFirst/Public/DbInitializers/SqliteInitializerBase.cs
+++ b/SQLite.CodeFirst/Public/DbInitializers/SqliteInitializerBase.cs
@@ -24,9 +24,10 @@ namespace SQLite.CodeFirst
public abstract class SqliteInitializerBase : IDatabaseInitializer
where TContext : DbContext
{
- protected SqliteInitializerBase(DbModelBuilder modelBuilder)
+ 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.
@@ -39,6 +40,7 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder)
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.
@@ -55,6 +57,8 @@ protected SqliteInitializerBase(DbModelBuilder modelBuilder)
}
}
+ public Collation DefaultCollation { get; }
+
protected DbModelBuilder ModelBuilder { get; }
///
@@ -71,7 +75,7 @@ public virtual void InitializeDatabase(TContext context)
string dbFile = GetDatabasePathFromContext(context);
InMemoryAwareFile.CreateDirectory(dbFile);
- var sqliteDatabaseCreator = new SqliteDatabaseCreator();
+ var sqliteDatabaseCreator = new SqliteDatabaseCreator(DefaultCollation);
sqliteDatabaseCreator.Create(context.Database, model);
Seed(context);
diff --git a/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs b/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs
index 4fd325e..0626c24 100644
--- a/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs
+++ b/SQLite.CodeFirst/Public/SqliteDatabaseCreator.cs
@@ -16,6 +16,13 @@ namespace SQLite.CodeFirst
///
public class SqliteDatabaseCreator : IDatabaseCreator
{
+ public SqliteDatabaseCreator(Collation defaultCollation = null)
+ {
+ DefaultCollation = defaultCollation;
+ }
+
+ public Collation DefaultCollation { get; }
+
///
/// Creates the SQLite-Database.
///
@@ -24,7 +31,7 @@ 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();
+ 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
index d0f248a..944115e 100644
--- a/SQLite.CodeFirst/Public/SqliteSqlGenerator.cs
+++ b/SQLite.CodeFirst/Public/SqliteSqlGenerator.cs
@@ -9,12 +9,19 @@ namespace SQLite.CodeFirst
///
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);
+ 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 21ebe59..a576f4f 100644
--- a/SQLite.CodeFirst/SQLite.CodeFirst.csproj
+++ b/SQLite.CodeFirst/SQLite.CodeFirst.csproj
@@ -1,16 +1,17 @@
- net40;net45
+ 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
+ true
+ snupkg
+ Apache-2.0
false
Copyright (C) Marc Sallin
SQLite EntityFramework EF CodeFirst
@@ -20,7 +21,7 @@
-
+
diff --git a/SQLite.CodeFirst/packages.lock.json b/SQLite.CodeFirst/packages.lock.json
new file mode 100644
index 0000000..9d27514
--- /dev/null
+++ b/SQLite.CodeFirst/packages.lock.json
@@ -0,0 +1,208 @@
+{
+ "version": 1,
+ "dependencies": {
+ ".NETFramework,Version=v4.0": {
+ "EntityFramework": {
+ "type": "Direct",
+ "requested": "[6.5.2, )",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA=="
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies": {
+ "type": "Direct",
+ "requested": "[1.0.3, )",
+ "resolved": "1.0.3",
+ "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+ "dependencies": {
+ "Microsoft.NETFramework.ReferenceAssemblies.net40": "1.0.3"
+ }
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies.net40": {
+ "type": "Transitive",
+ "resolved": "1.0.3",
+ "contentHash": "3ctXnCpHdoYJNH9ATfXKwckkkdHvHc1Xls12QB9kf1tjh3b+VzxtiwpkZN4GxOawakfH6CJAkqhlDKSiz6Ujbg=="
+ }
+ },
+ ".NETFramework,Version=v4.5": {
+ "EntityFramework": {
+ "type": "Direct",
+ "requested": "[6.5.2, )",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA=="
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies": {
+ "type": "Direct",
+ "requested": "[1.0.3, )",
+ "resolved": "1.0.3",
+ "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+ "dependencies": {
+ "Microsoft.NETFramework.ReferenceAssemblies.net45": "1.0.3"
+ }
+ },
+ "Microsoft.NETFramework.ReferenceAssemblies.net45": {
+ "type": "Transitive",
+ "resolved": "1.0.3",
+ "contentHash": "dcSLNuUX2rfZejsyta2EWZ1W5U6ucbFt697lRg1qiTlTM5ZlYv4uAvuxE6ROy6xLWWhLhOaReCDxkhxcajRYtQ=="
+ }
+ },
+ ".NETStandard,Version=v2.1": {
+ "EntityFramework": {
+ "type": "Direct",
+ "requested": "[6.5.2, )",
+ "resolved": "6.5.2",
+ "contentHash": "8iOcnaKcgkWh35s8EhknnENNwwqvrBJ7jeNbQITbGo1mIeIOhGPfVdNOXZ3Y496T6e6wfXvz0WeXlgMdcgVYSA==",
+ "dependencies": {
+ "Microsoft.CSharp": "4.7.0",
+ "System.CodeDom": "6.0.0",
+ "System.ComponentModel.Annotations": "5.0.0",
+ "System.Configuration.ConfigurationManager": "6.0.1",
+ "System.Data.SqlClient": "4.8.6"
+ }
+ },
+ "Microsoft.CSharp": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "pTj+D3uJWyN3My70i2Hqo+OXixq3Os2D1nJ2x92FFo6sk8fYS1m1WLNTs0Dc1uPaViH0YvEEwvzddQ7y4rhXmA=="
+ },
+ "Microsoft.Win32.Registry": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==",
+ "dependencies": {
+ "System.Buffers": "4.5.0",
+ "System.Memory": "4.5.3",
+ "System.Security.AccessControl": "4.7.0",
+ "System.Security.Principal.Windows": "4.7.0"
+ }
+ },
+ "runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "9kyFSIdN3T0qjDQ2R0HRXYIhS3l5psBzQi6qqhdLz+SzFyEy4sVxNOke+yyYv8Cu8rPER12c3RDjLT8wF3WBYQ==",
+ "dependencies": {
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0",
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0"
+ }
+ },
+ "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "LbrynESTp3bm5O/+jGL8v0Qg5SJlTV08lpIpFesXjF6uGNMWqFnUQbYBJwZTeua6E/Y7FIM1C54Ey1btLWupdg=="
+ },
+ "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "38ugOfkYJqJoX9g6EYRlZB5U2ZJH51UP8ptxZgdpS07FgOEToV+lS11ouNK2PM12Pr6X/PpT5jK82G3DwH/SxQ=="
+ },
+ "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "YhEdSQUsTx+C8m8Bw7ar5/VesXvCFMItyZF7G1AUY+OM0VPZUOeAVpJ4Wl6fydBGUYZxojTDR3I6Bj/+BPkJNA=="
+ },
+ "System.Buffers": {
+ "type": "Transitive",
+ "resolved": "4.5.1",
+ "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.ComponentModel.Annotations": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "dMkqfy2el8A8/I76n2Hi1oBFEbG1SfxD2l5nhwXV3XjlnOmwxJlQbYpJH4W51odnU9sARCSAgv7S3CyAFMkpYg=="
+ },
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "6.0.1",
+ "contentHash": "jXw9MlUu/kRfEU0WyTptAVueupqIeE3/rl0EZDMlf8pcvJnitQ8HeVEp69rZdaStXwTV72boi/Bhw8lOeO+U2w==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "6.0.0",
+ "System.Security.Permissions": "6.0.0"
+ }
+ },
+ "System.Data.SqlClient": {
+ "type": "Transitive",
+ "resolved": "4.8.6",
+ "contentHash": "2Ij/LCaTQRyAi5lAv7UUTV9R2FobC8xN9mE0fXBZohum/xLl8IZVmE98Rq5ugQHjCgTBRKqpXRb4ORulRdA6Ig==",
+ "dependencies": {
+ "Microsoft.Win32.Registry": "4.7.0",
+ "System.Buffers": "4.5.1",
+ "System.Diagnostics.DiagnosticSource": "4.7.0",
+ "System.Memory": "4.5.4",
+ "System.Security.Principal.Windows": "4.7.0",
+ "System.Text.Encoding.CodePages": "4.7.0",
+ "runtime.native.System.Data.SqlClient.sni": "4.7.0"
+ }
+ },
+ "System.Diagnostics.DiagnosticSource": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "oJjw3uFuVDJiJNbCD8HB4a2p3NYLdt1fiT5OGsPLw+WTOuG0KpP4OXelMmmVKpClueMsit6xOlzy4wNKQFiBLg==",
+ "dependencies": {
+ "System.Memory": "4.5.3"
+ }
+ },
+ "System.Memory": {
+ "type": "Transitive",
+ "resolved": "4.5.4",
+ "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==",
+ "dependencies": {
+ "System.Buffers": "4.5.1",
+ "System.Numerics.Vectors": "4.4.0",
+ "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+ }
+ },
+ "System.Numerics.Vectors": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+ },
+ "System.Runtime.CompilerServices.Unsafe": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "IpU1lcHz8/09yDr9N+Juc7SCgNUz+RohkCQI+KsWKR67XxpFr8Z6c8t1iENCXZuRuNCc4HBwme/MDHNVCwyAKg=="
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ==",
+ "dependencies": {
+ "System.Security.Principal.Windows": "5.0.0"
+ }
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ==",
+ "dependencies": {
+ "System.Memory": "4.5.4"
+ }
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
+ "dependencies": {
+ "System.Security.AccessControl": "6.0.0"
+ }
+ },
+ "System.Security.Principal.Windows": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
+ },
+ "System.Text.Encoding.CodePages": {
+ "type": "Transitive",
+ "resolved": "4.7.0",
+ "contentHash": "aeu4FlaUTemuT1qOd1MyU4T516QR4Fy+9yDbwWMPHOHy7U8FD6SgTzdZFO7gHcfAPHtECqInbwklVvUK4RHcNg==",
+ "dependencies": {
+ "System.Runtime.CompilerServices.Unsafe": "4.7.0"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/ci_appveyor.yml b/ci_appveyor.yml
deleted file mode 100644
index 3a96f64..0000000
--- a/ci_appveyor.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-version: CI_{branch}_{build}
-image: Visual Studio 2017
-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/global.json b/global.json
new file mode 100644
index 0000000..f272fe7
--- /dev/null
+++ b/global.json
@@ -0,0 +1,9 @@
+{
+ "sdk": {
+ "version": "10.0.301",
+ "rollForward": "latestMinor"
+ },
+ "test": {
+ "runner": "Microsoft.Testing.Platform"
+ }
+}
diff --git a/release_appveyor.yml b/release_appveyor.yml
deleted file mode 100644
index 17a1e42..0000000
--- a/release_appveyor.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-version: 1.5.2.{build}
-image: Visual Studio 2017
-branches:
- only:
- - master
-skip_tags: true
-platform: Any CPU
-configuration: Release
-dotnet_csproj:
- patch: true
- file: '**\*.csproj'
- version: '{version}'
- package_version: '{version}'
- assembly_version: '{version}'
- file_version: '{version}'
- informational_version: '{version}'
-before_build:
-- ps: dotnet --version
-- ps: dotnet restore
-build:
- project: SQLite.CodeFirst.sln
- parallel: true
- verbosity: normal
-artifacts:
-- path: SQLite.CodeFirst\bin
- name: Assemblies
-- path: SQLite.CodeFirst\bin\Release\*.nupkg
- name: NuPkg
-deploy:
-- provider: NuGet
- api_key:
- secure: QmbFnerlfTAFUZpnaPgVDywMH4fF8rVakefmqvhu3qm9SpuDlLGB9S4HwtdE3Nep
- on:
- branch: master
-- provider: GitHub
- tag: v$(appveyor_build_version)
- release: v$(appveyor_build_version)
- description: https://www.nuget.org/packages/SQLite.CodeFirst/
- auth_token:
- secure: e3cqaFy9PzI9TAdZJBIDy97Bfbwa7j0EXe2yw7Ev9aJXK0Q+3mnULqb1VU4P7BWR
- artifact: Assemblies,NuPkg
- draft: true
- on:
- branch: master
\ No newline at end of file