Зарегистрированное преобразование для Entity Framework Core в C# не вызывается при запросе с помощью Contains? Есть обходной путь? - PullRequest
0 голосов
/ 09 июля 2020

Я пытаюсь выяснить, почему преобразования, определенные для моего 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.ToLo wer (). Contains ("d") || s.FSharpOptionText .DefaultFromOption (). ToLower (). Contains ("d")) 'не может быть переведено.

Перепишите запрос в форме, которая может быть переведена, или переключитесь на оценку клиента явно, вставив вызов в AsEnumerable (), AsAsyncEnumerable (), ToList () или ToListAsyn c (). См. 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) в 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 [TResult] (запрос Expre synsion *, 1037 asynsion query *, 1037 asynsion query *, 10 в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCor e [TResult] (база данных IDatabase, запрос выражения, модель IModel, логический асинхронный c) в Microsoft.EntityFrameworkCore.Query.Internal.QueryCompilerplay. <> s__Dis12. 1) в Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQu ery [TResult] (Object cacheKey, Fun c1 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 (Cancella tionToken cancellationToken) в System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable 1.GetA syncEnumerator() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsy nc[TSource](IQueryable 1 источник, CancellationToken cancellationToken)

Для меня это не имеет особого смысла, поскольку предполагается, что преобразование будет применено :

Преобразователи значений позволяют преобразовывать значения свойств при чтении или записи в базу данных. Это преобразование может осуществляться от одного значения к другому того же типа (например, шифрование строк) или от значения одного типа к значению другого типа (например, преобразование значений перечисления в строки в базе данных и обратно) *. 1028 *

Если только «чтение» не применяется при «запросе», и если это действительно так, интересно, есть ли какое-нибудь обходное решение? Есть ли какой-нибудь механизм для расширения способа анализа выражения?

Между прочим, запрос ниже работает нормально:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...