我无法对由 ObjectTypeExtension 扩展的 EF 实体的导航属性应用 UseFiltering 方法。但是,当我禁用 ObjectTypeExtension 并直接在导航属性上的 EF 生成的类上应用 [UseFiltering()] 属性时,代码可以完美运行。当启用 ObjectExtensionType 时,这一挑战似乎特别明显。根本目标是避免 EF 实体和 HotChocolate 代码交织在一起,从而促使使用 ObjectExtensionType 来增强实体的功能 - 这是利用扩展类型背后的关键原理。
抛线错误:
descriptor.Field(t => t.Brands).UseFiltering();
错误信息:
1. Could not load type 'HotChocolate.Types.IInputType' from assembly 'HotChocolate.Types, Version=13.8.1.0, Culture=neutral, PublicKeyToken=null'. (Product.QL.Types.LobTeamType)
at HotChocolate.Configuration.TypeInitializer.DiscoverTypes()
at HotChocolate.Configuration.TypeInitializer.Initialize()
at HotChocolate.SchemaBuilder.Setup.InitializeTypes(SchemaBuilder builder, IDescriptorContext context, IReadOnlyList`1 types)
at HotChocolate.SchemaBuilder.Setup.Create(SchemaBuilder builder, LazySchema lazySchema, IDescriptorContext context)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaAsync(ConfigurationContext context, RequestExecutorSetup setup, RequestExecutorOptions executorOptions, IServiceProvider schemaServices, TypeModuleChangeMonitor typeModuleChangeMonitor, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.CreateSchemaServicesAsync(ConfigurationContext context, RequestExecutorSetup setup, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorNoLockAsync(String schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorResolver.GetRequestExecutorAsync(String schemaName, CancellationToken cancellationToken)
at HotChocolate.Execution.RequestExecutorProxy.GetRequestExecutorAsync(CancellationToken cancellationToken)
at HotChocolate.AspNetCore.HttpPostMiddlewareBase.HandleRequestAsync(HttpContext context)
at HotChocolate.AspNetCore.HttpPostMiddlewareBase.InvokeAsync(HttpContext context)
at Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.<>c__DisplayClass19_0.<<UseCancellation>b__1>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.ResponseCompression.ResponseCompressionMiddleware.InvokeCore(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
//EF Entity
public partial class LobTeam
{
public int LobTeamId { get; set; }
public Guid LobTeamUid { get; set; }
public string Name { get; set; } = null!;
public int StatusId { get; set; }
public virtual ICollection<LobTeamBrand> LobTeamBrands { get; set; } = new List<LobTeamBrand>();
public virtual ICollection<Platform> Platforms { get; set; } = new List<Platform>();
public virtual ICollection<Brand> Brands { get; set; }
}
//ObjectTypeExtension class
public sealed class LobTeamType : ObjectTypeExtension<LobTeam>
{
protected override void Configure(IObjectTypeDescriptor<LobTeam> descriptor)
{
descriptor
.Field(t => t.LobTeamBrands)
.Ignore();
descriptor
.Field(t => t.Brands)
.UseFiltering();
}
}
//Program.cs file
builder.Services
.AddGraphQLServer()
.AddQueryType<ProductQuery>()
.RegisterDbContext<ProductDataContext>(DbContextKind.Resolver)
.AddProjections()
.AddFiltering()
.AddSorting()
.AddTypeExtension<LobTeamType>();
显然不能在基本 EF 实体上使用 UseFiltering()。相反,HotChocolate 希望该字段属于 HotChocolate 对象扩展类型。因此,在上面的示例中,创建一个 BrandType objectExtension 并使用 BrandType 在 LobTeamType 上应用 UseFiltering()。 为了将其提升到一个新的水平,我创建了一个通用拦截器,它将创建对象扩展类型并在运行时应用 UseFiltering(),而不是为每种类型创建单独的类型。这帮助我对 ICollection 类型的所有导航属性应用过滤。这是它的代码。
public class FilterCollectionTypeInterceptor : TypeInterceptor
{
private static readonly List<string> _mappedTypes = new();
private static bool IsCollectionType(Type t)
=> t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>);
public override void OnBeforeRegisterDependencies(ITypeDiscoveryContext discoveryContext, DefinitionBase? definition)
{
if (definition is not ObjectTypeDefinition objectTypeDefinition) return;
if (objectTypeDefinition.RuntimeType.Name != "Object") return;
var keyBuilder = new StringBuilder();
for (var i = 0; i < objectTypeDefinition.Fields.Count; i++)
{
var field = objectTypeDefinition.Fields[i];
if (field.ResultType is null || !IsCollectionType(field.ResultType))
continue;
keyBuilder.Clear(); // Clear StringBuilder for the next key
keyBuilder.Append(objectTypeDefinition.Name.ToLower()).Append('.').Append(field.Name.ToLower());
var key = keyBuilder.ToString();
if (_mappedTypes.Contains(key))
continue;
var descriptor = field.ToDescriptor(discoveryContext.DescriptorContext);
descriptor.UseFiltering(typeof(FilterInputType<>).MakeGenericType(field.ResultType.GenericTypeArguments[0]));
objectTypeDefinition.Fields[i] = descriptor.ToDefinition();
_mappedTypes.Add(key);
}
}
}