Зарегистрированное преобразование для Entity Framework Core в C# не вызывается при запросе с помощью Contains? Есть обходной путь?
Я пытаюсь понять, почему конверсии, определенные для моего SqliteDbContext
ниже не рассматриваются, когда Contains
необходимо в запросе:
public static class FSharpOptionExtensions
{
public static T DefaultFromOption<T>(this FSharpOption<T> option)
where T : IEquatable<T> =>
(FSharpOption<T>.get_IsNone(option)
? default
: option.Value)!;
public static FSharpOption<T> ToOption<T>(this T source)
where T : IEquatable<T> =>
source.Equals(default)
? FSharpOption<T>.None
: FSharpOption<T>.Some(source);
}
public class SqliteDbContext : DbContext
{
public DbSet<SqliteRecord>? Records { get; set; }
public SqliteDbContext() // : base()
{
}
public SqliteDbContext(DbContextOptions<SqliteDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var faker = new Faker();
var recordCount = faker.Random.Int(500, 1000);
var records = Enumerable
.Range(1, recordCount)
.Select(index => new SqliteRecord
{
Id = index,
Integer = faker.Random.Long(),
Real = faker.Random.Double(),
NullableText = faker.Name.FullName().OrNull(faker, .2f),
NonNullableText = faker.Commerce.Product(),
FSharpOptionText = FSharpOption<string>.Some(faker.Vehicle.Manufacturer())
.OrDefault(faker, 0.5f, FSharpOption<string>.None)
});
modelBuilder
.Entity<SqliteRecord>()
.Property(x => x.FSharpOptionText)
.IsRequired(false)
.HasConversion(new ValueConverter<FSharpOption<string>?, string>(
option => option!.DefaultFromOption(),
value => value.ToOption()));
modelBuilder
.Entity<SqliteRecord>()
.HasKey(x => x.Id);
modelBuilder
.Entity<SqliteRecord>()
.ToTable("Records")
.HasData(records);
}
}
var records = await _dbContext.Records.Select(x => new
{
Id = x.Id,
Integer = x.Integer,
Real = x.Real,
NullableText = x.NullableText,
NonNullableText = x.NonNullableText,
FSharpOptionText = x.FSharpOptionText!.DefaultFromOption()
}).Where(s =>
s.NullableText!.ToLower().Contains("d") ||
s.NonNullableText!.ToLower().Contains("d") ||
s.FSharpOptionText.ToLower().Contains("d")
).ToListAsync();
Я получаю такую ошибку:
System.InvalidOperationException: выражение LINQ 'DbSet()
.Where (s => s.NullableText.ToLower (). Contains ("d") || s.NonNullableText.ToLower (). Contains ("d") || s.FSharpOptionText
.DefaultFromOption (). ToLower (). Contains ("d")) 'не может быть переведено.Либо перепишите запрос в форме, которая может быть переведена, либо явно переключитесь на оценку клиента, вставив вызов AsEnumerable(), AsAsyncEnumer able(), ToList() или ToListAsync(). См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.
в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVi sitor.g__CheckTranslated|15_0(ShapedQueryExpression переводится, <>c__DisplayClass15_0&)
в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVi sitor.VisitMethodCall(MethodCallExpression methodCallExpression)
в System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor Visi тор)
в System.Linq.Expressions.ExpressionVisitor.Visit (узел выражения) в Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVi sitor.VisitMethodCall(MethodCallExpression methodCallExpression) в System.Linq.Expression. Expression.AllExpression.ExpressionVisitor.Visit (узел выражения) в Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExe cutor[TResult](запрос выражения) в Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery [Microsoft asynsion query] (запрос Expre synsion,) EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCor e [TResult] (база данных IDatabase, запрос выражений, модель IModel, логическая асинхронность) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<> C__Display0Cla ss12_1.<ExecuteAsync>b__0() at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQu eryCore[TFunc](Object cacheKey, Func
1) в Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQu ery [TResult](Object cacheKey, Func1 compiler) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TR esult](Expression query, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAs ync[TResult](Expression expression, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable
1.GetAsyncEnu merator (CancellationToken cancellationToken) в System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable1.GetA syncEnumerator() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsy nc[TSource](IQueryable
1 источник, CancellationToken cancellationToken)
Для меня это не имеет особого смысла, поскольку предполагается, что преобразование применяется:
Конвертеры значений позволяют преобразовывать значения свойств при чтении из базы данных или записи в нее. Это преобразование может быть от одного значения к другому того же типа (например, шифрование строк) или от значения одного типа к значению другого типа (например, преобразование значений перечисления в строки в базе данных и обратно).
Если только "чтение" не применяется при "запросах", и если это действительно так, интересно, есть ли какое-то обходное решение? Любой механизм для расширения того, как анализируется выражение?
Кстати, приведенный ниже запрос работает нормально:
var records = await _dbContext.Records.Select(x => new
{
Id = x.Id,
Integer = x.Integer,
Real = x.Real,
NullableText = x.NullableText,
NonNullableText = x.NonNullableText,
FSharpOptionText = x.FSharpOptionText!.DefaultFromOption()
}).Where(x => x.Id == 1).ToListAsync();
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 5.0.0-preview.6.20312.4 initialized 'SqliteDbContext
' using provider 'Microsoft.EntityFrameworkCore.Sqlite' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeou
t='30']
SELECT "r"."Id", "r"."Integer", "r"."Real", "r"."NullableText", "r"."NonNu
llableText", "r"."FSharpOptionText"
FROM "Records" AS "r"
WHERE "r"."Id" = 1