Hotchocolate [использует артистику] / [UseFiltering] конфликт LINQ с ValueObjectC#

Место общения программистов C#
Ответить
Anonymous
 Hotchocolate [использует артистику] / [UseFiltering] конфликт LINQ с ValueObject

Сообщение Anonymous »

Using HotChocolate v15.1.8 with EF v9.0.7 and Mapster v7.4.0, I encounter a bug with [UseSorting] / [UseFiltering] LinQ conversions which appear invalid when applied to DDD ValueObject or EF complex property.
Executing this graphQL Query : < /p>

Код: Выделить всё

query Test {
legalEntity(where: { label: { eq: "TestSociety" } }) {
id
label
}
}
< /code>
дает мне следующую ошибку: < /p>
"extensions": {
"message": "The LINQ expression 'DbSet()\r\n    .Where(l => l.Label.Value == __p_0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.  See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.",
"stackTrace": "   at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.Translate(Expression expression)\r\n   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)\r\n   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)\r\n   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.c__DisplayClass11_0`1.b__0()\r\n at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)\r\n   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)\r\n   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()\r\n at HotChocolate.DefaultAsyncEnumerableExecutable`1.ToListAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.ListPostProcessor`1.ToCompletionResultAsync(Object result, CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)\r\n   at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
}
code
Чтобы получить этот результат, я использую hotchocolate

Код: Выделить всё

//Query
[Authorize]
[QueryType]
public class LegalEntityQueries
{
[UseFiltering]
[UseSorting]
public IQueryable GetLegalEntities(
AppDbContext dbContext,
int? limit = null,
int? offset = null
)
{
return dbContext.LegalEntities.TakeIf(limit).SkipIf(offset).ProjectToType();
}
}
< /code>
Чтобы избежать всей моей сущности, у меня есть GQL DTO с равными именами < /p>
//DTO
public record LegalEntityGql
{
public Guid Id { get; init; }
public string Label { get; init; } // As you can see, Label is here as a primitive string
}
< /code>
Чтобы преобразовать мою сущность в GQL, я использую Mapster v7.4.0 < /p>
//Mapster Config
internal sealed class ValueObjectMapsterConfig : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig().MapWith(l => l.Value);
}
}
< /code>
mapster и горячий шоколад настроены в моей программе.//Hot Chocolate config
public static class DependencyInjection
{
public static IServiceCollection AddGraphQL(this IServiceCollection services)
{
TypeAdapterConfig.GlobalSettings.Scan(typeof(ValueObjectMapsterConfig).Assembly);

services
.AddGraphQLServer()
.AddAuthorization()
.AddGraphQLTypes()
.AddProjections()
.AddFiltering()
.AddSorting();

MapsterConfiguration.RegisterMappings();

return services;
}
}
< /code>
Я следую над перспективой DDD, с доменом, включая агрегированные корни и объекты значения: < /p>
//Domain object
public class LegalEntity : BaseEntity
{
// 1. Properties
public Label Label { get; private set; }

// 2. Private constructor (used by factories)
private LegalEntity(Guid? id, Label labelt)
: base(id)
{
Label = label;
}

// 3.  Factory methods
public static Result Create(Label label) =>
CreateFull(null, label);

public static Result CreateFull(
Guid? id,
Label label,
)
{
//To be reused immediatly, each legalEntity should be created using a new Guid
Guid legalEntityId = id ?? Guid.NewGuid();

return new LegalEntity(legalEntityId, label);
}

// 4. Muleability methods (behavior/modification)
public void UpdateLabel(Label label)
{
Label = label;
}

// ORM constructor
private LegalEntity() { }
}
< /code>
Свойство моей метки предназначено как значение valueObject, чтобы сохранить неизменность внутри домена: < /p>
//ValueObject "Label"
public sealed class Label : ValueObject
{
public const int MaxLength = 50;

public string Value { get; }

private Label(string value)
{
Value = value;
}

public static Result Create(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return Result.Failure(DomainErrors.Label.Empty);
}

return new Label(value);
}

protected override IEnumerable GetAtomicValues()
{
yield return Value;
}

// ORM constructor
private Label() { }
}
< /code>
Тем не менее, EF сохраняет его как одну строку в SQL и конвертируйте его при чтении из DB: < /p>
public static class AdminModelBuilderExtension
{
public static void ConfigureAdminModule(this ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity
.Property(le => le.Label)
.HasConversion(le => le.Value, v => Label.Create(v).Value);

entity.HasIndex(le => le.Label).IsUnique();
});
}
}
Base of explanation
To read the GraphQL field Label, there is no problem using Mapster and the Label.Value conversion.
But UseSorting/UseFiltering are automatically converting the (where: { label: { eq: "TestSociety" } }) expression to an invalid LinQ последовательность.
Я воспроизвел проблему, построив 2 ef Queries: < /p>

Код: Выделить всё

LegalEntity? test1 = await dbContext
.LegalEntities.OrderBy(le => le.Label)
.FirstOrDefaultAsync(CancellationToken.None);

LegalEntity? test2 = await dbContext
.LegalEntities.OrderBy(le => le.Label.Value)
.FirstOrDefaultAsync(CancellationToken.None);
In this example, test1 works successfully (and it's fine!), but test2 emits the same error :

The LINQ expression 'DbSet()\r\n .OrderBy(le =>
le.Label.Value)' could not be translated.

Спасибо! < /p>

Подробнее здесь: https://stackoverflow.com/questions/797 ... alueobject
Ответить

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

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

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

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

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