FieldType BuildQueryField <TSource, TReturn>( string name, Func <ResolveFieldContext <TSource>, IQueryable <TReturn> > resolve, IEnumerable <QueryArgument> arguments, Type graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); graphType = GraphTypeFinder.FindGraphType <TReturn>(graphType); var listGraphType = MakeListGraphType(graphType); return(new FieldType { Name = name, Type = listGraphType, Arguments = ArgumentAppender.GetQueryArguments(arguments), Resolver = new AsyncFieldResolver <TSource, IEnumerable <TReturn> >( async context => { var returnTypes = resolve(context); var withIncludes = includeAppender.AddIncludes(returnTypes, context); var withArguments = withIncludes.ApplyGraphQlArguments(context); var list = await withArguments.ToListAsync(context.CancellationToken); return await filters.ApplyFilter(list, context.UserContext); }) }); }
ConnectionBuilder <FakeGraph, TSource> BuildListConnectionField <TSource, TReturn>( string name, Func <ResolveFieldContext <TSource>, IEnumerable <TReturn> > resolve, IEnumerable <string> includeName, int pageSize, Type graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); Guard.AgainstNegative(nameof(pageSize), pageSize); graphType = GraphTypeFinder.FindGraphType <TReturn>(graphType); var fieldType = GetFieldType <TSource>(name, graphType); var builder = ConnectionBuilder <FakeGraph, TSource> .Create(name); builder.PageSize(pageSize); SetField(builder, fieldType); IncludeAppender.SetIncludeMetadata(builder.FieldType, name, includeName); builder.ResolveAsync(async context => { var enumerable = resolve(context); enumerable = enumerable.ApplyGraphQlArguments(context); enumerable = await filters.ApplyFilter(enumerable, context.UserContext); var page = enumerable.ToList(); return(ConnectionConverter.ApplyConnectionContext( page, context.First, context.After, context.Last, context.Before)); }); return(builder); }
public void AddNavigationConnectionField <TSource, TReturn>( ComplexGraphType <TSource> graph, string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IEnumerable <TReturn> >?resolve = null, Type?itemGraphType = null, IEnumerable <QueryArgument>?arguments = null, IEnumerable <string>?includeNames = null, int pageSize = 10, string?description = null) where TReturn : class { Guard.AgainstNull(nameof(graph), graph); Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNegative(nameof(pageSize), pageSize); itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var fieldType = GetFieldType <TSource>(name, itemGraphType); var builder = ConnectionBuilder <TSource> .Create <FakeGraph>(name); SetField(builder, fieldType); if (description != null) { builder.Description(description); } builder.PageSize(pageSize).Bidirectional(); IncludeAppender.SetIncludeMetadata(builder.FieldType, name, includeNames); var hasId = keyNames.ContainsKey(typeof(TReturn)); if (resolve != null) { builder.ResolveAsync(async context => { var efFieldContext = BuildContext(context); var enumerable = resolve(efFieldContext); enumerable = enumerable.ApplyGraphQlArguments(hasId, context); enumerable = await efFieldContext.Filters.ApplyFilter(enumerable, context.UserContext); var page = enumerable.ToList(); return(ConnectionConverter.ApplyConnectionContext( page, context.First, context.After, context.Last, context.Before)); }); } var connection = builder; var field = graph.AddField(connection.FieldType); field.AddWhereArgument(hasId, arguments); }
FieldType BuildSingleField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, Task <IQueryable <TReturn> > > resolve, IEnumerable <QueryArgument>?arguments, Type?graphType, bool nullable) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); //lookup the graph type if not explicitly specified graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); //if not nullable, construct a non-null graph type for the specified graph type var wrappedType = nullable ? graphType : typeof(NonNullGraphType <>).MakeGenericType(graphType); //build the field return(new FieldType { Name = name, Type = wrappedType, //append the default query arguments to the specified argument list Arguments = ArgumentAppender.GetQueryArguments(arguments), //custom resolve function Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var efFieldContext = BuildContext(context); //get field names of the table's primary key(s) var names = GetKeyNames <TReturn>(); //run the specified resolve function var returnTypes = await resolve(efFieldContext); //include sub tables in the query based on the metadata stored for the requested graph var withIncludes = includeAppender.AddIncludes(returnTypes, context); //apply any query filters specified in the arguments var withArguments = withIncludes.ApplyGraphQlArguments(context, names); //run the query var single = await withArguments.SingleOrDefaultAsync(context.CancellationToken); //apply global filters to the returned value if (single != null) { if (await efFieldContext.Filters.ShouldInclude(context.UserContext, single)) { return single; } } //if no value was found, or if the returned value was filtered out by the global filters, // either return null, or throw an error if the field is not nullable if (nullable) { return null; } throw new ExecutionError("Not found"); }) }); }
static Type MakeListGraphType <TReturn>(Type?itemGraphType) where TReturn : class { if (itemGraphType == null) { itemGraphType = GraphTypeFinder.FindGraphType <TReturn>(); } return(nonNullType.MakeGenericType(listGraphType.MakeGenericType(itemGraphType))); }
FieldType BuildNavigationField <TSource, TReturn>( Type graphType, string name, Func <ResolveFieldContext <TSource>, IEnumerable <TReturn> > resolve, IEnumerable <string> includeNames, IEnumerable <QueryArgument> arguments) where TReturn : class { graphType = GraphTypeFinder.FindGraphType <TReturn>(graphType); var listGraphType = MakeListGraphType(graphType); return(BuildNavigationField(name, resolve, includeNames, listGraphType, arguments)); }
ConnectionBuilder <TSource> BuildQueryConnectionField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> >?resolve, int pageSize, Type?itemGraphType, string?description) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNegative(nameof(pageSize), pageSize); itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var fieldType = GetFieldType <TSource>(name, itemGraphType); var builder = ConnectionBuilder <TSource> .Create <FakeGraph>(name); SetField(builder, fieldType); if (description != null) { builder.Description(description); } builder.PageSize(pageSize).Bidirectional(); if (resolve != null) { builder.Resolve( context => { var efFieldContext = BuildContext(context); var query = resolve(efFieldContext); if (disableTracking) { query = query.AsNoTracking(); } query = includeAppender.AddIncludes(query, context); var names = GetKeyNames <TReturn>(); query = query.ApplyGraphQlArguments(context, names, true); return(query .ApplyConnectionContext( context.First, context.After, context.Last, context.Before, context, context.CancellationToken, efFieldContext.Filters)); }); } return(builder); }
ConnectionBuilder <FakeGraph, TSource> BuildQueryConnectionField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> > resolve, int pageSize, Type?graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); Guard.AgainstNegative(nameof(pageSize), pageSize); //lookup the graph type if not explicitly specified graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); //create a ConnectionBuilder<graphType, TSource> type by invoking the static Create method on the generic type var fieldType = GetFieldType <TSource>(name, graphType); //create a ConnectionBuilder<FakeGraph, TSource> which will be returned from this method var builder = ConnectionBuilder <FakeGraph, TSource> .Create(name); //set the page size builder.PageSize(pageSize); //using reflection, override the private field type property of the ConnectionBuilder<FakeGraph, TSource> to be the ConnectionBuilder<graphType, TSource> object SetField(builder, fieldType); //set the resolve function (note: this is not async capable) builder.Resolve( context => { //obtain the ef context var efFieldContext = BuildContext(context); //run the resolve function, then include the related tables on the returned query var withIncludes = includeAppender.AddIncludes(resolve(efFieldContext), context); //get field names of the table's primary key(s) var names = GetKeyNames <TReturn>(); //apply any query filters specified in the arguments var withArguments = withIncludes.ApplyGraphQlArguments(context, names); //apply skip/take to query as appropriate, and return the query return(withArguments .ApplyConnectionContext( context.First, context.After, context.Last, context.Before, context, context.CancellationToken, efFieldContext.Filters)); //note: does not apply global filters }); //return the field to be added to the graph return(builder); }
ConnectionBuilder <FakeGraph, TSource> BuildListConnectionField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IEnumerable <TReturn> > resolve, IEnumerable <string> includeName, int pageSize, Type graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); Guard.AgainstNegative(nameof(pageSize), pageSize); //lookup the graph type if not explicitly specified graphType = graphType ?? GraphTypeFinder.FindGraphType <TReturn>(); //create a ConnectionBuilder<graphType, TSource> type by invoking the static Create method on the generic type var fieldType = GetFieldType <TSource>(name, graphType); //create a ConnectionBuilder<FakeGraph, TSource> which will be returned from this method var builder = ConnectionBuilder <FakeGraph, TSource> .Create(name); //set the page size builder.PageSize(pageSize); //using reflection, override the private field type property of the ConnectionBuilder<FakeGraph, TSource> to be the ConnectionBuilder<graphType, TSource> object SetField(builder, fieldType); //add the metadata for the tables to be included in the query to the ConnectionBuilder<graphType, TSource> object IncludeAppender.SetIncludeMetadata(builder.FieldType, name, includeName); //set the custom resolver builder.ResolveAsync(async context => { var efFieldContext = BuildContext(context); //run the specified resolve function var enumerable = resolve(efFieldContext); //apply any query filters specified in the arguments enumerable = enumerable.ApplyGraphQlArguments(context); //apply the global filter on each individually enumerated item enumerable = await efFieldContext.Filters.ApplyFilter(enumerable, context.UserContext); //pagination does NOT occur server-side at this point, as the query has already executed var page = enumerable.ToList(); //return the proper page of data return(ConnectionConverter.ApplyConnectionContext( page, context.First, context.After, context.Last, context.Before)); }); //return the field to be added to the graph return(builder); }
public void AddQueryConnectionField <TSource, TReturn>( IComplexGraphType graph, string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> >?resolve = null, Type?itemGraphType = null, IEnumerable <QueryArgument>?arguments = null, int pageSize = 10, string?description = null) where TReturn : class { itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var addConnectionT = addQueryableConnection.MakeGenericMethod(typeof(TSource), itemGraphType, typeof(TReturn)); addConnectionT.Invoke(this, new object?[] { graph, name, resolve, arguments, pageSize, description }); }
ConnectionBuilder <TSource> BuildListConnectionField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IEnumerable <TReturn> >?resolve, IEnumerable <string>?includeName, int pageSize, Type?itemGraphType, string?description) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNegative(nameof(pageSize), pageSize); itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var fieldType = GetFieldType <TSource>(name, itemGraphType); var builder = ConnectionBuilder <TSource> .Create <FakeGraph>(name); if (description != null) { builder.Description(description); } builder.PageSize(pageSize); SetField(builder, fieldType); IncludeAppender.SetIncludeMetadata(builder.FieldType, name, includeName); if (resolve != null) { builder.ResolveAsync(async context => { var efFieldContext = BuildContext(context); var enumerable = resolve(efFieldContext); enumerable = enumerable.ApplyGraphQlArguments(context); enumerable = await efFieldContext.Filters.ApplyFilter(enumerable, context.UserContext); var page = enumerable.ToList(); return(ConnectionConverter.ApplyConnectionContext( page, context.First, context.After, context.Last, context.Before)); }); } return(builder); }
PaginationBuilder <TSource> BuildQueryPaginationField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> >?resolve, int page, int row, Type?itemGraphType, string?description) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNegative(nameof(page), page); Guard.AgainstNegative(nameof(row), row); itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var fieldType = GetPaginationFieldType <TSource>(name, itemGraphType); var builder = PaginationBuilder <TSource> .Create <FakeGraph>(name); if (description != null) { builder.Description(description); } builder.Page(page).Row(row); SetField(builder, fieldType); if (resolve != null) { builder.Resolve( context => { var efFieldContext = BuildContext(context); var query = resolve(efFieldContext); query = includeAppender.AddIncludes(query, context); var names = GetKeyNames <TReturn>(); query = query.ApplyGraphQlArguments(context, names); return(query .ApplyPaginationContext( context.Page, context.Row, context, context.CancellationToken, efFieldContext.Filters)); }); } return(builder); }
FieldType BuildQueryFieldAsync <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, Task <IQueryable <TReturn> > > resolve, IEnumerable <QueryArgument> arguments, Type graphType) where TReturn : class { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } if (resolve == null) { throw new ArgumentNullException(nameof(resolve)); } //lookup the graph type if not explicitly specified graphType = graphType ?? GraphTypeFinder.FindGraphType <TReturn>(); //create a list graph type based on the base field graph type var listGraphType = MakeListGraphType(graphType); //build the field return(new FieldType { Name = name, Type = listGraphType, //append the default query arguments to the specified argument list Arguments = ArgumentAppender.GetQueryArguments(arguments), //custom resolve function Resolver = new AsyncFieldResolver <TSource, IEnumerable <TReturn> >( async context => { //get field names of the table's primary key(s) var names = GetKeyNames <TReturn>(); //run the specified resolve function var returnTypes = await resolve(BuildEfContextFromGraphQlContext(context)); //include subtables in the query based on the metadata stored for the requested graph var withIncludes = includeAppender.AddIncludes(returnTypes, context); //apply any query filters specified in the arguments var withArguments = withIncludes.ApplyGraphQlArguments(context, names); //query the database var list = await withArguments.ToListAsync(context.CancellationToken); //apply the global filter on each individually enumerated item return filters == null ? list : await filters.ApplyFilter(list, context.UserContext); }) }); }
FieldType BuildNavigationField <TSource, TReturn>( Type graphType, string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IEnumerable <TReturn> > resolve, IEnumerable <string> includeNames, IEnumerable <QueryArgument> arguments) where TReturn : class { //lookup the graph type if not explicitly specified graphType = graphType ?? GraphTypeFinder.FindGraphType <TReturn>(); //graphType represents the base field type, not the list graph type //create a list graph type based on the graph type specified var listGraphType = MakeListGraphType(graphType); //build the navigation field return(BuildNavigationField(name, resolve, includeNames, listGraphType, arguments)); }
public FieldType AddNavigationField <TSource, TReturn>( ComplexGraphType <TSource> graph, string name, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn?>?resolve = null, Type?graphType = null, IEnumerable <string>?includeNames = null, string?description = null) where TReturn : class { Guard.AgainstNull(nameof(graph), graph); Guard.AgainstNullWhiteSpace(nameof(name), name); graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); FieldType field = new() { Name = name, Type = graphType, Description = description }; IncludeAppender.SetIncludeMetadata(field, name, includeNames); if (resolve != null) { field.Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var fieldContext = BuildContext(context); var result = resolve(fieldContext); if (await fieldContext.Filters.ShouldInclude(context.UserContext, result)) { return(result); } return(null); }); } return(graph.AddField(field)); } }
public void AddNavigationConnectionField <TSource, TReturn>( ComplexGraphType <TSource> graph, string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IEnumerable <TReturn> >?resolve = null, Type?itemGraphType = null, IEnumerable <QueryArgument>?arguments = null, IEnumerable <string>?includeNames = null, int pageSize = 10, string?description = null) where TReturn : class { Guard.AgainstWhiteSpace(nameof(name), name); Guard.AgainstNegative(nameof(pageSize), pageSize); itemGraphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var addConnectionT = addEnumerableConnection.MakeGenericMethod(typeof(TSource), itemGraphType, typeof(TReturn)); addConnectionT.Invoke(this, new object?[] { graph, name, resolve, pageSize, description, arguments, includeNames }); }
FieldType BuildQueryField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, Task <IQueryable <TReturn> > > resolve, IEnumerable <QueryArgument>?arguments, Type?graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); //lookup the graph type if not explicitly specified graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); //create a list graph type based on the base field graph type var listGraphType = MakeListGraphType(graphType); //build the field return(new FieldType { Name = name, Type = listGraphType, //append the default query arguments to the specified argument list Arguments = ArgumentAppender.GetQueryArguments(arguments), //custom resolve function Resolver = new AsyncFieldResolver <TSource, IEnumerable <TReturn> >( async context => { var efFieldContext = BuildContext(context); //get field names of the table's primary key(s) var names = GetKeyNames <TReturn>(); //run the specified resolve function var query = await resolve(efFieldContext); //include sub tables in the query based on the metadata stored for the requested graph query = includeAppender.AddIncludes(query, context); //apply any query filters specified in the arguments query = query.ApplyGraphQlArguments(context, names); //query the database var list = await query.ToListAsync(context.CancellationToken); //apply the global filter on each individually enumerated item return await efFieldContext.Filters.ApplyFilter(list, context.UserContext); }) }); }
FieldType BuildNavigationField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn> resolve, IEnumerable <string> includeNames, Type graphType) where TReturn : class { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } if (resolve == null) { throw new ArgumentNullException(nameof(resolve)); } //lookup the graph type if not explicitly specified graphType = graphType ?? GraphTypeFinder.FindGraphType <TReturn>(); //build field return(new FieldType { Name = name, Type = graphType, //add the metadata for the tables to be included in the query Metadata = IncludeAppender.GetIncludeMetadata(name, includeNames), //custom resolve function simply applies the global filters; typically it's a pass-through Resolver = new AsyncFieldResolver <TSource, TReturn>(async context => { //run resolve function var result = resolve(BuildEfContextFromGraphQlContext(context)); //apply global filters and return null if necessary if (filters == null || await filters.ShouldInclude(context.UserContext, result)) { return result; } return null; }) }); }
FieldType BuildSingleField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, Task <IQueryable <TReturn> > > resolve, IEnumerable <QueryArgument> arguments, Type graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); graphType = GraphTypeFinder.FindGraphType <TReturn>(graphType); var notNullGraph = typeof(NonNullGraphType <>); var wrappedType = notNullGraph.MakeGenericType(graphType); return(new FieldType { Name = name, Type = wrappedType, Arguments = ArgumentAppender.GetQueryArguments(arguments), Resolver = new AsyncFieldResolver <TSource, TReturn>( async context => { var returnTypes = await resolve(BuildEfContextFromGraphQlContext(context)); var withIncludes = includeAppender.AddIncludes(returnTypes, context); var names = GetKeyNames <TReturn>(); var withArguments = withIncludes.ApplyGraphQlArguments(context, names); var single = await withArguments.SingleOrDefaultAsync(context.CancellationToken); if (single != null) { if (await filters.ShouldInclude(context.UserContext, single)) { return single; } } throw new ExecutionError("Not found"); }) }); }
FieldType BuildNavigationField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn?>?resolve, IEnumerable <string>?includeNames, Type?graphType, string?description) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); var fieldType = new FieldType { Name = name, Type = graphType, Metadata = IncludeAppender.GetIncludeMetadata(name, includeNames), Description = description }; if (resolve != null) { fieldType.Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var efFieldContext = BuildContext(context); var result = resolve(efFieldContext); if (await efFieldContext.Filters.ShouldInclude(context.UserContext, result)) { return(result); } return(null); }); } return(fieldType); }
ConnectionBuilder <FakeGraph, TSource> BuildQueryConnectionField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> > resolve, int pageSize, Type graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); Guard.AgainstNegative(nameof(pageSize), pageSize); graphType = GraphTypeFinder.FindGraphType <TReturn>(graphType); var fieldType = GetFieldType <TSource>(name, graphType); var builder = ConnectionBuilder <FakeGraph, TSource> .Create(name); builder.PageSize(pageSize); SetField(builder, fieldType); builder.Resolve( context => { var efFieldContext = BuildEfContextFromGraphQlContext(context); var withIncludes = includeAppender.AddIncludes(resolve(efFieldContext), context); var names = GetKeyNames <TReturn>(); var withArguments = withIncludes.ApplyGraphQlArguments(context, names); return(withArguments .ApplyConnectionContext( context.First, context.After, context.Last, context.Before, context, context.CancellationToken, filters)); }); return(builder); }
FieldType BuildNavigationField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn?> resolve, IEnumerable <string>?includeNames, Type?graphType) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); //lookup the graph type if not explicitly specified graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); //build field return(new FieldType { Name = name, Type = graphType, //add the metadata for the tables to be included in the query Metadata = IncludeAppender.GetIncludeMetadata(name, includeNames), //custom resolve function simply applies the global filters; typically it's a pass-through Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var efFieldContext = BuildContext(context); //run resolve function var result = resolve(efFieldContext); //apply global filters and return null if necessary if (await efFieldContext.Filters.ShouldInclude(context.UserContext, result)) { return result; } return null; }) }); }
FieldType BuildSingleField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> > resolve, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn, Task>?mutate, IEnumerable <QueryArgument>?arguments, Type?graphType, bool nullable, string?description) where TReturn : class { Guard.AgainstNullWhiteSpace(nameof(name), name); Guard.AgainstNull(nameof(resolve), resolve); graphType ??= GraphTypeFinder.FindGraphType <TReturn>(); Type?wrappedType; if (nullable) { wrappedType = graphType; } else { wrappedType = typeof(NonNullGraphType <>).MakeGenericType(graphType); } return(new FieldType { Name = name, Type = wrappedType, Description = description, Arguments = ArgumentAppender.GetQueryArguments(arguments), Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var efFieldContext = BuildContext(context); var names = GetKeyNames <TReturn>(); var query = resolve(efFieldContext); if (disableTracking) { query = query.AsNoTracking(); } query = includeAppender.AddIncludes(query, context); query = query.ApplyGraphQlArguments(context, names); var single = await query.SingleOrDefaultAsync(context.CancellationToken); if (single != null) { if (await efFieldContext.Filters.ShouldInclude(context.UserContext, single)) { if (mutate != null) { await mutate.Invoke(efFieldContext, single); } return single; } } if (nullable) { return null; } throw new("Not found"); }) });
FieldType BuildSingleField <TSource, TReturn>( string name, Func <ResolveEfFieldContext <TDbContext, TSource>, IQueryable <TReturn> > resolve, Func <ResolveEfFieldContext <TDbContext, TSource>, TReturn, Task>?mutate, IEnumerable <QueryArgument>?arguments, Type?graphType, bool nullable, string?description) where TReturn : class { Guard.AgainstWhiteSpace(nameof(name), name); graphType ??= GraphTypeFinder.FindGraphType <TReturn>(nullable); var hasId = keyNames.ContainsKey(typeof(TReturn)); return(new() { Name = name, Type = graphType, Description = description, Arguments = ArgumentAppender.GetQueryArguments(arguments, hasId, false), Resolver = new AsyncFieldResolver <TSource, TReturn?>( async context => { var efFieldContext = BuildContext(context); var names = GetKeyNames <TReturn>(); var query = resolve(efFieldContext); if (disableTracking) { query = query.AsNoTracking(); } query = includeAppender.AddIncludes(query, context); query = query.ApplyGraphQlArguments(context, names, false); QueryLogger.Write(query); var single = await query.SingleOrDefaultAsync(context.CancellationToken); if (single is not null) { if (await efFieldContext.Filters.ShouldInclude(context.UserContext, single)) { if (mutate is not null) { await mutate.Invoke(efFieldContext, single); } return single; } } if (nullable) { return null; } throw new ExecutionError("Not found"); }) }); }