Skip to content

Commit

Permalink
Completion of Case Insensitivity code.
Browse files Browse the repository at this point in the history
  • Loading branch information
makingbloke committed Feb 11, 2025
1 parent 0028c8f commit c7c914a
Show file tree
Hide file tree
Showing 13 changed files with 127 additions and 155 deletions.
8 changes: 7 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### Version 4.0.1.0 - 10 February 2025

* Added new Case Insensitivity Extensions which provide an upgrade to SQLite's NOCASE collation and methods to set a case insensitive default collation sequence.
* Updated the MSTest Nuget package.
* More Test refactoring and tidying.

### Version 4.0.0.1 - 04 February 2025

* Fixed issue where `ExecuteUpdateGetRows` was not finding any rows to update due to an issue with parameter types when run against SQLite.
Expand All @@ -23,7 +29,7 @@
`<TProperty>` = Property type (used in execute update code only).
* Added guard clauses to public methods.
* **Breaking Change** Removed TableExists methods as these did not support schemas and can be easily replicated in standard EF Core.
* Enabled nullable reference types in both projects and corrected any issues assocated with this.
* Enabled nullable reference types in both projects and corrected any issues associated with this.
* **Breaking Change** Removed `DbContext GetContext<TEntity>(this IQueryable<TEntity> query)` as it required a reflection hack which made it unsupportable between EF versions.
* **Breaking Change** Altered `GetUniqueConstraintDetails` and `GetUniqueConstraintDetailsAsync` to take the database facade as their first parameter.
* Refactored tests to make the results easier to read.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using DotDoc.EntityFrameworkCore.Extensions.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Extensions;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Data;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Utilities;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -72,7 +71,9 @@ public void Test_UseSqlServerCaseInsensitiveCollation_GuardClause()
public void Test_UseSqliteUnicodeNoCase()
{
// ARRANGE / ACT
using Context context = DatabaseUtils.CreateDatabase(DatabaseType.Sqlite, useSqliteCaseInsensitivityInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
DatabaseType.Sqlite,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseSqliteUnicodeNoCase());

string originalValue = new('\u00E1', 10); // \u00E1 = Latin Small Letter A with Acute.
string searchValue = new('A', 10);
Expand All @@ -97,22 +98,52 @@ public void Test_UseSqliteUnicodeNoCase_NonSqlite()
string message = "Unsupported database type";

// ACT / ASSERT
InvalidOperationException e = Assert.ThrowsException<InvalidOperationException>(() => DatabaseUtils.CreateDatabase(DatabaseType.SqlServer, useSqliteCaseInsensitivityInterceptor: true), "Unexpected exception");
InvalidOperationException e = Assert.ThrowsException<InvalidOperationException>(
() =>
{
using Context context = DatabaseUtils.CreateDatabase(
DatabaseType.SqlServer,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseSqliteUnicodeNoCase());
},
"Unexpected exception");

Assert.AreEqual(message, e.Message, "Invalid exception message");
}

/// <summary>
/// Test UseSqlite/SqlServerCaseInsensitiveCollation.
/// Test UseSqliteCaseInsensitiveCollation.
/// </summary>
[TestMethod("UseSqliteCaseInsensitiveCollation")]
public void Test_UseSqliteCaseInsensitiveCollation()
{
// ARRANGE / ACT
using Context context = DatabaseUtils.CreateDatabase(
DatabaseType.Sqlite,
customModelCreationActions: (modelBuilder) => modelBuilder.UseSqliteCaseInsensitiveCollation());

string originalValue = new('a', 10);
string searchValue = originalValue.ToUpperInvariant();

DatabaseUtils.CreateTestTableEntries(context, originalValue, 1);

// ASSERT
List<TestTable1> results = context.TestTable1
.Where(e => e.TestField == searchValue)
.ToList();

Assert.IsTrue(results.Count > 0, "Invalid record count");
}

/// <summary>
/// Test UseSqlServerCaseInsensitiveCollation.
/// </summary>
/// <param name="databaseType">Database type.</param>
/// <param name="collation">Default Collation Sequence.</param>
[TestMethod("UseCaseInsensitiveCollation")]
[DataRow(DatabaseType.Sqlite, DefaultCollationSequence.SqliteCaseInsensitive, DisplayName = DatabaseType.Sqlite)]
[DataRow(DatabaseType.SqlServer, DefaultCollationSequence.SqlServerCaseInsensitive, DisplayName = DatabaseType.SqlServer)]
public void Test_UseCaseInsensitiveCollation(string databaseType, DefaultCollationSequence collation)
[TestMethod("UseSqlServerCaseInsensitiveCollation")]
public void Test_UseSqlServerCaseInsensitiveCollation()
{
// ARRANGE / ACT
using Context context = DatabaseUtils.CreateDatabase(databaseType, collation: collation);
using Context context = DatabaseUtils.CreateDatabase(
DatabaseType.SqlServer,
customModelCreationActions: (modelBuilder) => modelBuilder.UseSqlServerCaseInsensitiveCollation());

string originalValue = new('a', 10);
string searchValue = originalValue.ToUpperInvariant();
Expand Down

This file was deleted.

67 changes: 12 additions & 55 deletions DotDoc.EntityFrameworkCore.Extensions.Tests/Data/Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
// See the License.txt file in the solution root for more information.

using DotDoc.EntityFrameworkCore.Extensions.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Extensions;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Constants;
using Microsoft.EntityFrameworkCore;

namespace DotDoc.EntityFrameworkCore.Extensions.Tests.Data;
Expand All @@ -25,24 +23,14 @@ public class Context : DbContext
private readonly string _connectionString;

/// <summary>
/// If <see langword="true"/> use the Execute Update Interceptor.
/// Custom Configuration Actions..
/// </summary>
private readonly bool _useExecuteUpdateInterceptor;
private readonly Action<DbContextOptionsBuilder>? _customConfigurationActions;

/// <summary>
/// If <see langword="true"/> use the SQLite Case Insensitivity Interceptor.
/// Custom Configuration Actions..
/// </summary>
private readonly bool _useSqliteCaseInsensitivityInterceptor;

/// <summary>
/// If <see langword="true"/> use the Unique Constraint Interceptor.
/// </summary>
private readonly bool _useUniqueConstraintInterceptor;

/// <summary>
/// Default Collation Sequence.
/// </summary>
private readonly DefaultCollationSequence _collation;
private readonly Action<ModelBuilder>? _customModelCreationActions;

#endregion private fields

Expand All @@ -53,17 +41,13 @@ public class Context : DbContext
/// </summary>
/// <param name="databaseType">Database Type.</param>
/// <param name="connectionString">Database Connection String.</param>
/// <param name="useExecuteUpdateInterceptor">If <see langword="true"/> use the Execute Update Interceptor.</param>
/// <param name="useSqliteCaseInsensitivityInterceptor">If <see langword="true"/> use the SQLite Case Insensitivity Interceptor.</param>
/// <param name="useUniqueConstraintInterceptor">If <see langword="true"/> use the Unique Constraint Interceptor.</param>
/// <param name="collation">Default Collation Sequence.</param>
/// <param name="customConfigurationActions">Custom Configuration Actions (optional).</param>
/// <param name="customModelCreationActions">Custom Model Creation Actions (optional).</param>
public Context(
string databaseType,
string connectionString,
bool useExecuteUpdateInterceptor = false,
bool useSqliteCaseInsensitivityInterceptor = false,
bool useUniqueConstraintInterceptor = false,
DefaultCollationSequence collation = DefaultCollationSequence.None)
Action<DbContextOptionsBuilder>? customConfigurationActions = null,
Action<ModelBuilder>? customModelCreationActions = null)
{
if (string.IsNullOrEmpty(connectionString))
{
Expand All @@ -72,10 +56,8 @@ public Context(

this._databaseType = databaseType;
this._connectionString = connectionString;
this._useExecuteUpdateInterceptor = useExecuteUpdateInterceptor;
this._useSqliteCaseInsensitivityInterceptor = useSqliteCaseInsensitivityInterceptor;
this._useUniqueConstraintInterceptor = useUniqueConstraintInterceptor;
this._collation = collation;
this._customConfigurationActions = customConfigurationActions;
this._customModelCreationActions = customModelCreationActions;
}

#endregion public constructors
Expand Down Expand Up @@ -115,39 +97,14 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
throw new InvalidOperationException("Unsupported database type");
}

if (this._useExecuteUpdateInterceptor)
{
optionsBuilder.UseExecuteUpdateExtensions();
}

if (this._useSqliteCaseInsensitivityInterceptor)
{
optionsBuilder.UseSqliteUnicodeNoCase();
}

if (this._useUniqueConstraintInterceptor)
{
optionsBuilder.UseUniqueConstraintInterceptor();
}
this._customConfigurationActions?.Invoke(optionsBuilder);
}
}

/// <inheritdoc/>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
switch (this._collation)
{
case DefaultCollationSequence.SqliteCaseInsensitive:
modelBuilder.UseSqliteCaseInsensitiveCollation();
break;

case DefaultCollationSequence.SqlServerCaseInsensitive:
modelBuilder.UseSqlServerCaseInsensitiveCollation();
break;

default:
break;
}
this._customModelCreationActions?.Invoke(modelBuilder);
}

#endregion protected methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<Company>dotDoc Systems</Company>
<Copyright>Copyright ©2021-2025 Mike King.</Copyright>
<Description>EntityFameworkCore.Extensions Tests.</Description>
<VersionPrefix>4.0.0.1</VersionPrefix>
<VersionPrefix>4.0.1.0</VersionPrefix>
</PropertyGroup>

<ItemGroup>
Expand All @@ -34,7 +34,7 @@
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.7.3" />
<PackageReference Include="MSTest.TestFramework" Version="3.7.3" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ public async Task Test_ExecuteUpdateGetCount_GuardClausesAsync(IQueryable<TestTa
public void Test_ExecuteUpdateGetCount(string databaseType, int count)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useExecuteUpdateInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseExecuteUpdateExtensions());

string value = TestUtils.GetMethodName();
string originalValue = $"Original {value}";
Expand Down Expand Up @@ -123,7 +125,9 @@ public void Test_ExecuteUpdateGetCount(string databaseType, int count)
public async Task Test_ExecuteUpdateGetCountAsync(string databaseType, int count)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useExecuteUpdateInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseExecuteUpdateExtensions());

string value = TestUtils.GetMethodName();
string originalValue = $"Original {value}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ public async Task Test_ExecuteUpdateGetRows_GuardClausesAsync(IQueryable<TestTab
public void Test_ExecuteUpdateGetRows(string databaseType, int count)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useExecuteUpdateInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseExecuteUpdateExtensions());

string value = TestUtils.GetMethodName();
string originalValue = $"Original {value}";
Expand Down Expand Up @@ -134,7 +136,9 @@ public void Test_ExecuteUpdateGetRows(string databaseType, int count)
public async Task Test_ExecuteUpdateGetRowsAsync(string databaseType, int count)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useExecuteUpdateInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseExecuteUpdateExtensions());

string value = TestUtils.GetMethodName();
string originalValue = $"Original {value}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using DotDoc.EntityFrameworkCore.Extensions.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Exceptions;
using DotDoc.EntityFrameworkCore.Extensions.Extensions;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Data;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;
Expand Down Expand Up @@ -33,7 +34,9 @@ public class UniqueConstraintInterceptorTests
public void Test_UniqueConstraintInterceptor(string databaseType)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useUniqueConstraintInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseUniqueConstraintInterceptor());

string? schema = DatabaseUtils.GetDefaultSchema(databaseType);
string value = TestUtils.GetMethodName();
Expand Down Expand Up @@ -72,7 +75,9 @@ public void Test_UniqueConstraintInterceptor(string databaseType)
public async Task Test_UniqueConstraintInterceptorAsync(string databaseType)
{
// ARRANGE
using Context context = DatabaseUtils.CreateDatabase(databaseType, useUniqueConstraintInterceptor: true);
using Context context = DatabaseUtils.CreateDatabase(
databaseType,
customConfigurationActions: (optionsBuilder) => optionsBuilder.UseUniqueConstraintInterceptor());

string? schema = DatabaseUtils.GetDefaultSchema(databaseType);
string value = TestUtils.GetMethodName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// See the License.txt file in the solution root for more information.

using DotDoc.EntityFrameworkCore.Extensions.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Constants;
using DotDoc.EntityFrameworkCore.Extensions.Tests.Data;
using Microsoft.EntityFrameworkCore;

Expand All @@ -20,17 +19,13 @@ public static class DatabaseUtils
/// Create an a test database.
/// </summary>
/// <param name="databaseType">The type of database to create.</param>
/// <param name="useExecuteUpdateInterceptor">If <see langword="true"/> use the Execute Update Interceptor.</param>
/// <param name="useSqliteCaseInsensitivityInterceptor">If <see langword="true"/> use the SQLite Case Insensitivity Interceptor.</param>
/// <param name="useUniqueConstraintInterceptor">If <see langword="true"/> use the Unique Constraint Interceptor.</param>
/// <param name="collation">Default Collation Sequence.</param>
/// <param name="customConfigurationActions">Custom Configuration Actions (optional).</param>
/// <param name="customModelCreationActions">Custom Model Creation Actions (optional).</param>
/// <returns>An instance of <see cref="Context"/> for the database.</returns>
public static Context CreateDatabase(
string databaseType,
bool useExecuteUpdateInterceptor = false,
bool useSqliteCaseInsensitivityInterceptor = false,
bool useUniqueConstraintInterceptor = false,
DefaultCollationSequence collation = DefaultCollationSequence.None)
Action<DbContextOptionsBuilder>? customConfigurationActions = null,
Action<ModelBuilder>? customModelCreationActions = null)
{
Context context;

Expand All @@ -41,10 +36,8 @@ public static Context CreateDatabase(
context = new(
databaseType,
"Data Source = :memory:",
useExecuteUpdateInterceptor,
useSqliteCaseInsensitivityInterceptor,
useUniqueConstraintInterceptor,
collation);
customConfigurationActions,
customModelCreationActions);

context.Database.OpenConnection();
context.Database.EnsureCreated();
Expand All @@ -56,10 +49,8 @@ public static Context CreateDatabase(
context = new(
databaseType,
"Server=localhost;Initial Catalog=DotDoc.EntityFrameworkCore.Extensions.Tests;Trusted_Connection=True;TrustServerCertificate=True",
useExecuteUpdateInterceptor,
useSqliteCaseInsensitivityInterceptor,
useUniqueConstraintInterceptor,
collation);
customConfigurationActions,
customModelCreationActions);

context.Database.EnsureDeleted();
context.Database.EnsureCreated();
Expand Down
Loading

0 comments on commit c7c914a

Please sign in to comment.