Сопоставление определяемых пользователем функций EF: «Убедитесь, что параметр может быть сопоставлен текущим поставщикомC#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Сопоставление определяемых пользователем функций EF: «Убедитесь, что параметр может быть сопоставлен текущим поставщиком

Сообщение Anonymous »

У меня есть собственный тип UniversityId (тип значения):
namespace TestEntityFramework;

public sealed class UniversityId(string value)
{
public string Value { get; } = value;

public override string ToString()
{
return Value;
}
}

public static class UniversityIdExtensions
{
public static bool IsForeignUniversity(this UniversityId universityId)
{
return universityId.ToString().Contains('-');
}
}

Это контекст моей базы данных:
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

namespace TestEntityFramework;

public class DatabaseContext(DbContextOptions options) : DbContext(options)
{
public DbSet Students { get; set; }

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
base.ConfigureConventions(configurationBuilder);
configurationBuilder.Properties().HaveConversion();
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity().HasKey(s => s.Id);

MethodInfo methodInfo = typeof(UniversityIdExtensions)
.GetMethod(name: nameof(UniversityIdExtensions.IsForeignUniversity), types: [typeof(UniversityId)])!;

modelBuilder
.HasDbFunction(methodInfo)
.HasTranslation(args =>
{
ISqlExpressionFactory sqlExpressionFactory = this.GetService();
SqlConstantExpression likePattern = sqlExpressionFactory.Constant(value: "%-%");
return sqlExpressionFactory.Like(match: args[0], pattern: likePattern);
});
}
}

А это конвертер:
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

namespace TestEntityFramework;

public sealed class UniversityIdConverter() : ValueConverter(
convertToProviderExpression: id => id.Value, convertFromProviderExpression: id => new UniversityId(id));

Я часто использовал ValueConverter в своих приложениях DDD, но впервые пытаюсь создать собственную DbFunction. использовать следующим образом:
public static IResult Handler(DatabaseContext context)
{
IQueryable query = context.Students
.Where(s => s.UniversityId.IsForeignUniversity())
.Select(s => new Response(s.Id, s.Name, s.Country, s.UniversityId.ToString()));
return Results.Ok(query);
}

Я постоянно получаю следующую ошибку, хотя конверсия определенно есть:
Unable to create a 'DbContext' of type 'TestEntityFramework.DatabaseContext'. The exception 'The parameter 'universityId' for the DbFunction 'TestEntityFramework.UniversityIdExtensions.IsForeignUniversity(TestEntityFramework.Uni versityId)' has an invalid type 'UniversityId'. Ensure the parameter type can be mapped by the current provider.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https: //go.microsoft.com/fwlink/?linkid=851728
Это кажется достаточно простым (это минимальный воспроизводимый пример, который я мог бы привести), однако независимо от того, как я пытаюсь выполнить перевод, он терпит неудачу (я также пробовал так ):
.HasTranslation(args => new SqlFunctionExpression(
functionName: "LIKE",
arguments:
[
args[0],
new SqlConstantExpression(
constantExpression: Expression.Constant("%-%"),
typeMapping: new StringTypeMapping(storeType: "nvarchar(max)", dbType: null)
)
],
nullable: false,
argumentsPropagateNullability: [false, false],
type: typeof(bool),
typeMapping: RelationalTypeMapping.NullMapping));

Но результат тот же. Может ли кто-нибудь указать мне в правильном направлении? Большое вам спасибо.
EDIT: Код после комментария @Charlieface:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity().HasKey(s => s.Id);
modelBuilder.Entity().Property(s => s.UniversityId).HasMaxLength(32);

MethodInfo methodInfo = typeof(UniversityIdExtensions)
.GetMethod(name: nameof(UniversityIdExtensions.IsForeignUniversity), types: [typeof(UniversityId)])!;

modelBuilder
.HasDbFunction(methodInfo: methodInfo,
builderAction: dbf =>
{
dbf.HasParameter(name: "universityId", buildAction: p => p.HasStoreType("varchar(32)"))
.HasTranslation(args =>
{
ISqlExpressionFactory sqlExpressionFactory = this.GetService();
SqlConstantExpression likePattern = sqlExpressionFactory.Constant(value: "%-%");
return sqlExpressionFactory.Like(match: args[0], pattern: likePattern);
});
});
}

Это работает (т. е. миграция создана), но я получаю исключение ObjectDisposeException при вызове конечной точки, которая использует пользовательскую функцию.
РЕДАКТИРОВАТЬ 2: Похоже, это произошло из-за того, как я применил миграцию. Однако я получил следующее:
Invalid cast from 'System.String' to 'TestEntityFramework.UniversityId'.

Поэтому я не уверен, что это сработало, хотя я мог выполнить миграцию.
РЕДАКТИРОВАТЬ 3: Объединив оба подхода, я получил это, что почти работает, но мне нужно изменить знак равенства на НРАВИТСЯ:
modelBuilder
.HasDbFunction(methodInfo: methodInfo,
builderAction: dbf =>
{
dbf.HasParameter(name: "universityId", buildAction: p => p.HasStoreType("varchar(32)"))
.HasTranslation(args =>
{
var universityId = args[0];

var likeExpression = new SqlBinaryExpression(
ExpressionType.Equal,
universityId,
new SqlConstantExpression(
Expression.Constant("%-%"),
new StringTypeMapping("varchar(32)", dbType: null)
),
typeof(bool),
new BoolTypeMapping("bool")
);

return likeExpression;
});
});

А это сгенерированный SQL:
SELECT `s`.`Id`, `s`.`Name`, `s`.`Country`, `s`.`UniversityId`
FROM `Students` AS `s`
WHERE `s`.`UniversityId` = '%-%'


Подробнее здесь: https://stackoverflow.com/questions/787 ... by-the-cur
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»