提交 983f2750 编写于 作者: J Jean-Philippe Leconte 提交者: Shay Rojansky

Added fuzzystrmatch module plugin (#1124)

上级 79b9e966
......@@ -28,6 +28,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.PG.NTS", "src\EFCore
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.PG.Trigrams", "src\EFCore.PG.Trigrams\EFCore.PG.Trigrams.csproj", "{4DEDE46C-FABB-4AD3-A7DF-BF4A3AF00B06}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.PG.FuzzyStringMatch", "src\EFCore.PG.FuzzyStringMatch\EFCore.PG.FuzzyStringMatch.csproj", "{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -110,6 +112,18 @@ Global
{4DEDE46C-FABB-4AD3-A7DF-BF4A3AF00B06}.Release|x64.Build.0 = Release|Any CPU
{4DEDE46C-FABB-4AD3-A7DF-BF4A3AF00B06}.Release|x86.ActiveCfg = Release|Any CPU
{4DEDE46C-FABB-4AD3-A7DF-BF4A3AF00B06}.Release|x86.Build.0 = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|x64.ActiveCfg = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|x64.Build.0 = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|x86.ActiveCfg = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Debug|x86.Build.0 = Debug|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|Any CPU.Build.0 = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|x64.ActiveCfg = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|x64.Build.0 = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|x86.ActiveCfg = Release|Any CPU
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -122,6 +136,7 @@ Global
{77F0608F-6D0C-481C-9108-D5176E2EAD69} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
{D7106D61-C7CA-4005-B31F-43281BB397AD} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
{4DEDE46C-FABB-4AD3-A7DF-BF4A3AF00B06} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
{86525F23-3EB0-4B0D-9BBB-55FF247D4DD6} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F4EAAE6D-758C-4184-9D8C-7113384B61A8}
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>Jean-Philippe Leconte</Authors>
<Description>fuzzystrmatch module support plugin for PostgreSQL/Npgsql Entity Framework Core provider.</Description>
<PackageTags>npgsql;postgresql;postgres;Entity Framework Core;entity-framework-core;ef;efcore;orm;sql;fuzzystrmatch</PackageTags>
<AssemblyName>Npgsql.EntityFrameworkCore.PostgreSQL.FuzzyStringMatch</AssemblyName>
<RootNamespace>Npgsql.EntityFrameworkCore.PostgreSQL.FuzzyStringMatch</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\EFCore.PG\EFCore.PG.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\*.cs" />
<None Include="build\**\*">
<Pack>True</Pack>
<PackagePath>build</PackagePath>
</None>
</ItemGroup>
</Project>
using Microsoft.EntityFrameworkCore.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal;
namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// fuzzystrmatch module specific extension methods for <see cref="NpgsqlDbContextOptionsBuilder"/>.
/// </summary>
public static class NpgsqlFuzzyStringMatchDbContextOptionsBuilderExtensions
{
/// <summary>
/// Enable fuzzystrmatch module methods.
/// </summary>
/// <param name="optionsBuilder">The build being used to configure Postgres.</param>
/// <returns>The options builder so that further configuration can be chained.</returns>
public static NpgsqlDbContextOptionsBuilder UseFuzzyStringMatch(
this NpgsqlDbContextOptionsBuilder optionsBuilder)
{
var coreOptionsBuilder = ((IRelationalDbContextOptionsBuilderInfrastructure)optionsBuilder).OptionsBuilder;
var extension = coreOptionsBuilder.Options.FindExtension<NpgsqlFuzzyStringMatchOptionsExtension>()
?? new NpgsqlFuzzyStringMatchOptionsExtension();
((IDbContextOptionsBuilderInfrastructure)coreOptionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder;
}
}
}
using System;
namespace Microsoft.EntityFrameworkCore
{
public static class NpgsqlFuzzyStringMatchDbFunctionsExtensions
{
/// <summary>
/// The soundex function converts a string to its Soundex code.
/// </summary>
/// <remarks>
/// The method call is translated to <c>soundex(text)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static string FuzzyStringMatchSoundex(this DbFunctions _, string text) =>
throw new NotSupportedException();
/// <summary>
/// The difference function converts two strings to their Soundex codes and
/// then returns the number of matching code positions. Since Soundex codes
/// have four characters, the result ranges from zero to four, with zero being
/// no match and four being an exact match.
/// </summary>
/// <remarks>
/// The method call is translated to <c>difference(source, target)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static int FuzzyStringMatchDifference(this DbFunctions _, string source, string target) =>
throw new NotSupportedException();
/// <summary>
/// Returns the Levenshtein distance between two strings.
/// </summary>
/// <remarks>
/// The method call is translated to <c>levenshtein(source, target)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static int FuzzyStringMatchLevenshtein(this DbFunctions _, string source, string target) =>
throw new NotSupportedException();
/// <summary>
/// Returns the Levenshtein distance between two strings.
/// </summary>
/// <remarks>
/// The method call is translated to <c>levenshtein(source, target, insertionCost, deletionCost, substitutionCost)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static int FuzzyStringMatchLevenshtein(this DbFunctions _, string source, string target, int insertionCost, int deletionCost, int substitutionCost) =>
throw new NotSupportedException();
/// <summary>
/// levenshtein_less_equal is an accelerated version of the Levenshtein function for use when only small distances are of interest.
/// If the actual distance is less than or equal to maximum distance, then levenshtein_less_equal returns the correct distance;
/// otherwise it returns some value greater than maximum distance. If maximum distance is negative then the behavior is the same as levenshtein.
/// </summary>
/// <remarks>
/// The method call is translated to <c>levenshtein_less_equal(source, target, maximumDistance)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static int FuzzyStringMatchLevenshteinLessEqual(this DbFunctions _, string source, string target, int maximumDistance) =>
throw new NotSupportedException();
/// <summary>
/// levenshtein_less_equal is an accelerated version of the Levenshtein function for use when only small distances are of interest.
/// If the actual distance is less than or equal to maximum distance, then levenshtein_less_equal returns the correct distance;
/// otherwise it returns some value greater than maximum distance. If maximum distance is negative then the behavior is the same as levenshtein.
/// </summary>
/// <remarks>
/// The method call is translated to <c>levenshtein_less_equal(source, target, insertionCost, deletionCost, substitutionCost, maximumDistance)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static int FuzzyStringMatchLevenshteinLessEqual(this DbFunctions _, string source, string target, int insertionCost, int deletionCost, int substitutionCost, int maximumDistance) =>
throw new NotSupportedException();
/// <summary>
/// The metaphone function converts a string to its Metaphone code.
/// </summary>
/// <remarks>
/// The method call is translated to <c>metaphone(text, maximumOutputLength)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static string FuzzyStringMatchMetaphone(this DbFunctions _, string text, int maximumOutputLength) =>
throw new NotSupportedException();
/// <summary>
/// The dmetaphone function converts a string to its primary Double Metaphone code.
/// </summary>
/// <remarks>
/// The method call is translated to <c>dmetaphone(text)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static string FuzzyStringMatchDoubleMetaphone(this DbFunctions _, string text) =>
throw new NotSupportedException();
/// <summary>
/// The dmetaphone_alt function converts a string to its alternate Double Metaphone code.
/// </summary>
/// <remarks>
/// The method call is translated to <c>dmetaphone_alt(text)</c>.
///
/// See https://www.postgresql.org/docs/current/fuzzystrmatch.html.
/// </remarks>
public static string FuzzyStringMatchDoubleMetaphoneAlt(this DbFunctions _, string text) =>
throw new NotSupportedException();
}
}
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// fuzzystrmatch module extension methods for <see cref="IServiceCollection"/>.
/// </summary>
public static class NpgsqlFuzzyStringMatchServiceCollectionExtensions
{
/// <summary>
/// Adds the services required for fuzzystrmatch support in the Npgsql provider for Entity Framework.
/// </summary>
/// <param name="serviceCollection">The <see cref="IServiceCollection"/> to add services to.</param>
/// <returns>The same service collection so that multiple calls can be chained.</returns>
public static IServiceCollection AddEntityFrameworkNpgsqlFuzzyStringMatch(
this IServiceCollection serviceCollection)
{
new EntityFrameworkRelationalServicesBuilder(serviceCollection)
.TryAddProviderSpecificServices(
x => x.TryAddSingletonEnumerable<IMethodCallTranslatorPlugin, NpgsqlFuzzyStringMatchMethodCallTranslatorPlugin>());
return serviceCollection;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.DependencyInjection;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure.Internal
{
public class NpgsqlFuzzyStringMatchOptionsExtension : IDbContextOptionsExtension
{
public DbContextOptionsExtensionInfo Info => new ExtensionInfo(this);
public virtual void ApplyServices(IServiceCollection services) => services.AddEntityFrameworkNpgsqlFuzzyStringMatch();
public virtual void Validate(IDbContextOptions options)
{
var internalServiceProvider = options.FindExtension<CoreOptionsExtension>()?.InternalServiceProvider;
if (internalServiceProvider != null)
{
using var scope = internalServiceProvider.CreateScope();
var plugins = scope.ServiceProvider.GetService<IEnumerable<IMethodCallTranslatorPlugin>>();
if (plugins is null || !plugins.Any(s => s is NpgsqlFuzzyStringMatchMethodCallTranslatorPlugin))
{
throw new InvalidOperationException($"{nameof(NpgsqlFuzzyStringMatchDbContextOptionsBuilderExtensions.UseFuzzyStringMatch)} requires {nameof(NpgsqlFuzzyStringMatchServiceCollectionExtensions.AddEntityFrameworkNpgsqlFuzzyStringMatch)} to be called on the internal service provider used.");
}
}
}
sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
public ExtensionInfo(IDbContextOptionsExtension extension)
: base(extension)
{
}
public override bool IsDatabaseProvider => false;
public override long GetServiceProviderHashCode() => 0;
public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
=> debugInfo["Npgsql:" + nameof(NpgsqlFuzzyStringMatchDbContextOptionsBuilderExtensions.UseFuzzyStringMatch)] = "1";
public override string LogFragment => "using FuzzyStringMatch ";
}
}
}
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal
{
public class NpgsqlFuzzyStringMatchMethodCallTranslatorPlugin : IMethodCallTranslatorPlugin
{
public NpgsqlFuzzyStringMatchMethodCallTranslatorPlugin(
IRelationalTypeMappingSource typeMappingSource,
ISqlExpressionFactory sqlExpressionFactory)
=> Translators = new IMethodCallTranslator[]
{
new NpgsqlFuzzyStringMatchMethodTranslator((NpgsqlSqlExpressionFactory) sqlExpressionFactory, typeMappingSource),
};
public virtual IEnumerable<IMethodCallTranslator> Translators { get; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.ExpressionTranslators.Internal
{
public class NpgsqlFuzzyStringMatchMethodTranslator : IMethodCallTranslator
{
static readonly Dictionary<MethodInfo, string> Functions = new Dictionary<MethodInfo, string>
{
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchSoundex), new[] { typeof(DbFunctions), typeof(string) })] = "soundex",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDifference), new[] { typeof(DbFunctions), typeof(string), typeof(string) })] = "difference",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshtein), new[] { typeof(DbFunctions), typeof(string), typeof(string) })] = "levenshtein",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshtein), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int), typeof(int), typeof(int) })] = "levenshtein",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshteinLessEqual), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int) })] = "levenshtein_less_equal",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchLevenshteinLessEqual), new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(int), typeof(int), typeof(int), typeof(int) })] = "levenshtein_less_equal",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchMetaphone), new[] { typeof(DbFunctions), typeof(string), typeof(int) })] = "metaphone",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDoubleMetaphone), new[] { typeof(DbFunctions), typeof(string) })] = "dmetaphone",
[GetRuntimeMethod(nameof(NpgsqlFuzzyStringMatchDbFunctionsExtensions.FuzzyStringMatchDoubleMetaphoneAlt), new[] { typeof(DbFunctions), typeof(string) })] = "dmetaphone_alt"
};
static MethodInfo GetRuntimeMethod(string name, params Type[] parameters)
=> typeof(NpgsqlFuzzyStringMatchDbFunctionsExtensions).GetRuntimeMethod(name, parameters);
readonly NpgsqlSqlExpressionFactory _sqlExpressionFactory;
public NpgsqlFuzzyStringMatchMethodTranslator(NpgsqlSqlExpressionFactory sqlExpressionFactory, IRelationalTypeMappingSource typeMappingSource)
=> _sqlExpressionFactory = sqlExpressionFactory;
/// <inheritdoc />
public SqlExpression Translate(SqlExpression instance, MethodInfo method, IReadOnlyList<SqlExpression> arguments)
=> Functions.TryGetValue(method, out var function)
? _sqlExpressionFactory.Function(function, arguments.Skip(1), method.ReturnType)
: null;
}
}
......@@ -9,6 +9,7 @@
<ProjectReference Include="..\..\src\EFCore.PG\EFCore.PG.csproj" />
<ProjectReference Include="..\..\src\EFCore.PG.NTS\EFCore.PG.NTS.csproj" />
<ProjectReference Include="..\..\src\EFCore.PG.Trigrams\EFCore.PG.Trigrams.csproj" />
<ProjectReference Include="..\..\src\EFCore.PG.FuzzyStringMatch\EFCore.PG.FuzzyStringMatch.csproj" />
</ItemGroup>
<ItemGroup>
......
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities.Xunit;
using Xunit;
using Xunit.Abstractions;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query
{
/// <summary>
/// Provides unit tests for the fuzzystrmatch module function translations.
/// </summary>
/// <remarks>
/// See: https://www.postgresql.org/docs/current/fuzzystrmatch.html
/// </remarks>
public class FuzzyStringMatchQueryNpgsqlTest : IClassFixture<FuzzyStringMatchQueryNpgsqlTest.FuzzyStringMatchQueryNpgsqlFixture>
{
FuzzyStringMatchQueryNpgsqlFixture Fixture { get; }
public FuzzyStringMatchQueryNpgsqlTest(FuzzyStringMatchQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper)
{
Fixture = fixture;
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
#region FunctionTests
[Fact]
public void FuzzyStringMatchSoundex()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchSoundex(x.Text))
.ToArray();
AssertContainsSql(@"soundex(f.""Text"")");
}
[Fact]
public void FuzzyStringMatchDifference()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchDifference(x.Text, "target"))
.ToArray();
AssertContainsSql(@"difference(f.""Text"", 'target')");
}
[Fact]
public void FuzzyStringMatchLevenshtein()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchLevenshtein(x.Text, "target"))
.ToArray();
AssertContainsSql(@"levenshtein(f.""Text"", 'target')");
}
[Fact]
public void FuzzyStringMatchLevenshtein_With_Costs()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchLevenshtein(x.Text, "target", 1, 2, 3))
.ToArray();
AssertContainsSql(@"levenshtein(f.""Text"", 'target', 1, 2, 3)");
}
[Fact]
public void FuzzyStringMatchLevenshteinLessEqual()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchLevenshteinLessEqual(x.Text, "target", 5))
.ToArray();
AssertContainsSql(@"levenshtein_less_equal(f.""Text"", 'target', 5)");
}
[Fact]
public void FuzzyStringMatchLevenshteinLessEqual_With_Costs()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchLevenshteinLessEqual(x.Text, "target", 1, 2, 3, 5))
.ToArray();
AssertContainsSql(@"levenshtein_less_equal(f.""Text"", 'target', 1, 2, 3, 5)");
}
[Fact]
public void FuzzyStringMatchMetaphone()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchMetaphone(x.Text, 6))
.ToArray();
AssertContainsSql(@"metaphone(f.""Text"", 6)");
}
[Fact]
public void FuzzyStringMatchDoubleMetaphone()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchDoubleMetaphone(x.Text))
.ToArray();
AssertContainsSql(@"dmetaphone(f.""Text"")");
}
[Fact]
public void FuzzyStringMatchDoubleMetaphoneAlt()
{
using var context = CreateContext();
var _ = context.FuzzyStringMatchTestEntities
.Select(x => EF.Functions.FuzzyStringMatchDoubleMetaphoneAlt(x.Text))
.ToArray();
AssertContainsSql(@"dmetaphone_alt(f.""Text"")");
}
#endregion
#region Fixtures
/// <summary>
/// Represents a fixture suitable for testing fuzzy string match functions.
/// </summary>
public class FuzzyStringMatchQueryNpgsqlFixture : SharedStoreFixtureBase<FuzzyStringMatchContext>
{
protected override string StoreName => "FuzzyStringMatchQueryTest";
protected override IServiceCollection AddServices(IServiceCollection serviceCollection)
=> base.AddServices(serviceCollection).AddEntityFrameworkNpgsqlFuzzyStringMatch();
public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder)
{
var optionsBuilder = base.AddOptions(builder);
new NpgsqlDbContextOptionsBuilder(optionsBuilder).UseFuzzyStringMatch();
return optionsBuilder;
}
protected override ITestStoreFactory TestStoreFactory => NpgsqlTestStoreFactory.Instance;
public TestSqlLoggerFactory TestSqlLoggerFactory => (TestSqlLoggerFactory)ListLoggerFactory;
protected override void Seed(FuzzyStringMatchContext context) => FuzzyStringMatchContext.Seed(context);
}
/// <summary>
/// Represents an entity suitable for testing fuzzy string match functions.
/// </summary>
public class FuzzyStringMatchTestEntity
{
// ReSharper disable once UnusedMember.Global
/// <summary>
/// The primary key.
/// </summary>
[Key]
public int Id { get; set; }
/// <summary>
/// Some text.
/// </summary>
public string Text { get; set; }
}
/// <summary>
/// Represents a database suitable for testing fuzzy string match functions.
/// </summary>
public class FuzzyStringMatchContext : PoolableDbContext
{
/// <summary>
/// Represents a set of entities with <see cref="System.String"/> properties.
/// </summary>
public DbSet<FuzzyStringMatchTestEntity> FuzzyStringMatchTestEntities { get; set; }
/// <summary>
/// Initializes a <see cref="FuzzyStringMatchContext"/>.
/// </summary>
/// <param name="options">
/// The options to be used for configuration.
/// </param>
public FuzzyStringMatchContext(DbContextOptions options) : base(options) {}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasPostgresExtension("fuzzystrmatch");
base.OnModelCreating(modelBuilder);
}
public static void Seed(FuzzyStringMatchContext context)
{
for (var i = 1; i <= 9; i++)
{
var text = "Some text " + i;
context.FuzzyStringMatchTestEntities.Add(
new FuzzyStringMatchTestEntity
{
Id = i,
Text = text
});
}
context.SaveChanges();
}
}
#endregion
#region Helpers
protected FuzzyStringMatchContext CreateContext() => Fixture.CreateContext();
void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
/// <summary>
/// Asserts that the SQL fragment appears in the logs.
/// </summary>
/// <param name="sql">The SQL statement or fragment to search for in the logs.</param>
void AssertContainsSql(string sql) => Assert.Contains(sql, Fixture.TestSqlLoggerFactory.Sql);
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册