C# EF核心与GraphQL
我目前正在探索GraphQL开发,我目前正在探索通过EF Core生成什么样的SQL查询,我观察到,无论我的GraphQL查询只包含几个字段,EF Core都会为实体的所有字段发送SQL Select 这是我现在使用的代码:C# EF核心与GraphQL,c#,asp.net-core,entity-framework-core,graphql,graphql-dotnet,C#,Asp.net Core,Entity Framework Core,Graphql,Graphql Dotnet,我目前正在探索GraphQL开发,我目前正在探索通过EF Core生成什么样的SQL查询,我观察到,无论我的GraphQL查询只包含几个字段,EF Core都会为实体的所有字段发送SQL Select 这是我现在使用的代码: public class DoctorType : ObjectGraphType<Doctors> { public DoctorType() { Field(d => d.PrefixTitle);
public class DoctorType : ObjectGraphType<Doctors>
{
public DoctorType()
{
Field(d => d.PrefixTitle);
Field(d => d.FName);
Field(d => d.MName);
Field(d => d.LName);
Field(d => d.SufixTitle);
Field(d => d.Image);
Field(d => d.EGN);
Field(d => d.Description);
Field(d => d.UID_Code);
}
}
public class Doctors : ApplicationUser
{
public string Image { get; set; }
[StringLength(50)]
public string UID_Code { get; set; }
}
生成的SQL选择医生实体的所有字段
是否有任何方法可以进一步优化从EF Core生成的SQL查询
我猜这是因为DoctorType继承自ObjectGraphType
,而不是医生的某个投影,但我想不出一个聪明的解决方法
有什么建议吗
编辑:
我正在使用Joe McBride版本2.4.0的GraphQL.NET(GraphQL dotnet)
编辑2:
要么我做错了,要么我不知道
作为其中一条建议,我下载了SimonCropp提供的GraphQL.EntityFramework Nuget包
我完成了它所需的所有配置:
services.AddDbContext<ScheduleDbContext>(options =>
{
options.UseMySql(Configuration.GetConnectionString("DefaultConnection"));
});
using (var myDataContext = new ScheduleDbContext())
{
EfGraphQLConventions.RegisterInContainer(services, myDataContext);
}
调试日志显示生成的SQL查询是
SELECT `s`.`SpecializationId`, `s`.`Code`, `s`.`SpecializationName`
FROM `Specializations` AS `s`
尽管我只需要specializationName字段,而且我希望它是:
SELECT `s`.`SpecializationName`
FROM `Specializations` AS `s`
更新
我想到目前为止我还不明白graphQL到底是如何工作的。我认为有一些幕后的数据获取,但没有
主提取在查询的字段解析程序中完成:
FieldAsync<ListGraphType<DoctorType>>("doctors", resolve: async ctx => await doctorServices.ListAsync());
专门化类型
public class SpecializationType : EfObjectGraphType<Specializations>
{
public SpecializationType(IEfGraphQLService graphQlService
, IDataLoaderContextAccessor accessor, IDoctorGraphQlServices doctorServices)
: base(graphQlService)
{
Field(p => p.SpecializationId);
Field(p => p.Code);
Field(p => p.SpecializationName);
Field<ListGraphType<DoctorType>, IEnumerable<Doctors>>()
.Name("doctors")
.ResolveAsync(ctx =>
{
var selectedFields = GraphQLResolverContextHelpers.GetFirstLevelLeavesNamesPascalCase(ctx.SubFields);
selectedFields = GraphQLResolverContextHelpers.AppendParrentNodeToEachItem(selectedFields, parentNode: "Doctor");
selectedFields = selectedFields.Union(new[] { "Specializations_SpecializationId" });
var expression = BuildLinqSelectorObject.BuildSelector<SpecializationsDoctors, SpecializationsDoctors>(selectedFields);
var doctorsLoader = accessor.Context
.GetOrAddCollectionBatchLoader<int, Doctors>(
"GetDoctorsBySpecializationId"
, (collection, token) =>
{
return doctorServices.GetDoctorsBySpecializationIdAsync(collection, token, expression);
});
return doctorsLoader.LoadAsync(ctx.Source.SpecializationId);
});
}
}
公共类SpecializationType:eObjectGraphType
{
公共专门化类型(IEfGraphQLService graphQlService
,IDataLoaderContextAccessor访问器,IDoctorGraphQlServices博士服务)
:base(graphQlService)
{
字段(p=>p.SpecializationId);
字段(p=>p.Code);
字段(p=>p.SpecializationName);
字段()
.姓名(“医生”)
.ResolveAsync(ctx=>
{
var selectedFields=GraphQLResolverContextHelpers.getFirstLevelLeavesNamePascalCase(ctx.SubFields);
selectedFields=GraphQLResolverContextHelpers.AppendParrentNodeToEachItem(selectedFields,parentNode:“医生”);
selectedFields=selectedFields.Union(新[]{“Specializations\u SpecializationId”});
var expression=BuildLinqSelectorObject.BuildSelector(selectedFields);
var doctorsLoader=accessor.Context
.GetOradCollectionBatchLoader(
“GetDoctorBySpecialization ID”
,(集合、令牌)=>
{
返回doctorServices.GetDoctorsBySpecializationIdAsync(集合、令牌、表达式);
});
返回doctorsLoader.LoadAsync(ctx.Source.SpecializationId);
});
}
}
医生服务:
public class DoctorGraphQlServices : IDoctorGraphQlServices
{
public ScheduleDbContext _dbContext { get; set; }
public DoctorGraphQlServices(ScheduleDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<Doctors>> ListAsync(int? specializationId = null)
{
var doctors = _dbContext.Doctors.AsQueryable();
if(specializationId != null)
{
doctors = doctors.Where(d => d.Specializations.Any(s => s.Specializations_SpecializationId == specializationId));
}
return await doctors.ToListAsync();
}
public async Task<ILookup<int, Doctors>> GetDoctorsBySpecializationIdAsync(IEnumerable<int> specializationIds, CancellationToken token, Expression<Func<SpecializationsDoctors, SpecializationsDoctors>> selector = null)
{
var doctors = await _dbContext.SpecializationsDoctors
.Include(s => s.Doctor)
.Where(spDocs => specializationIds.Any(sp => sp == spDocs.Specializations_SpecializationId))
.Select(selector: selector)
.ToListAsync();
return doctors.ToLookup(i => i.Specializations_SpecializationId, i => i.Doctor);
}
}
公共类DoctorGraphQlServices:IDoctorGraphQlServices
{
公共ScheduleDbContext _dbContext{get;set;}
公共DoctorGraphQlServices(ScheduleDbContext-dbContext)
{
_dbContext=dbContext;
}
公共异步任务ListAsync(int?specializationId=null)
{
var doctors=_dbContext.doctors.AsQueryable();
if(specializationId!=null)
{
医生=医生。其中(d=>d.Specializations.Any(s=>s.Specializations\U SpecializationId==SpecializationId));
}
返回等待医生。ToListSync();
}
公共异步任务GetDoctorBySpecializationIDASync(IEnumerable SpecializationId、CancellationToken令牌、表达式选择器=null)
{
var医生=等待_dbContext.SpecializationsDoctors
.包括(s=>s.Doctor)
.Where(spDocs=>SpecializationId.Any(sp=>sp==spDocs.Specializations\U SpecializationId))
.选择(选择器:选择器)
.ToListAsync();
返回documents.ToLookup(i=>i.Specializations\u SpecializationId,i=>i.Doctor);
}
}
专业服务
public class SpeciaizationGraphQlServices : ISpecializationGraphQlServices
{
public ScheduleDbContext _dbContext { get; set; }
public SpeciaizationGraphQlServices(ScheduleDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<dynamic> ListAsync(string doctorId = null, Expression<Func<Specializations, Specializations>> selector = null)
{
var specializations = _dbContext.Specializations.AsQueryable();
if (!string.IsNullOrEmpty(doctorId))
{
specializations = specializations.Where(s => s.Doctors.Any(d => d.Doctors_Id == doctorId));
}
return await specializations.Select(selector).ToListAsync();
}
public async Task<ILookup<string, Specializations>> GetSpecializationsByDoctorIdAsync(IEnumerable<string> doctorIds, CancellationToken token)
{
var specializations = await _dbContext.SpecializationsDoctors
.Include(s => s.Specialization)
.Where(spDocs => doctorIds.Any(sp => sp == spDocs.Doctors_Id))
.ToListAsync();
return specializations.ToLookup(i => i.Doctors_Id, i => i.Specialization);
}
public IQueryable<Specializations> List(string doctorId = null)
{
var specializations = _dbContext.Specializations.AsQueryable();
if (!string.IsNullOrEmpty(doctorId))
{
specializations = specializations.Where(s => s.Doctors.Any(d => d.Doctors_Id == doctorId));
}
return specializations;
}
}
公共类SpecializationGraphQlServices:isSpecializationGraphQlServices
{
公共ScheduleDbContext _dbContext{get;set;}
公共SpecializationGraphQLServices(ScheduleDbContext dbContext)
{
_dbContext=dbContext;
}
公共异步任务ListAsync(字符串doctorId=null,表达式选择器=null)
{
var specializations=_dbContext.specializations.AsQueryable();
如果(!string.IsNullOrEmpty(doctorId))
{
专科=专科。其中(s=>s.Doctors.Any(d=>d.Doctors\u Id==doctorId));
}
return wait specializations.Select(选择器).toListSync();
}
公共异步任务GetSpecializationsByDoctorIdAsync(IEnumerable doctorIds,CancellationToken token)
{
var specializations=await\u dbContext.SpecializationsDoctors
.包括(s=>s.专业化)
.Where(spDocs=>doctorIds.Any(sp=>sp==spDocs.Doctors\u Id))
.ToListAsync();
返回specializations.ToLookup(i=>i.Doctors\u Id,i=>i.Specialization);
}
公共IQueryable列表(字符串doctorId=null)
{
var specializations=_dbContext.specializations.AsQueryable();
如果(!string.IsNullOrEmpty(doctorId))
{
专科=专科。其中(s=>s.Doctors.Any(d=>d.Doctors\u Id==doctorId));
}
返回专业化;
}
}
这篇文章已经变得相当大,很抱歉出现了span..对于
DoctorType
,请检查定义的ObjectGraphType
,它用于返回Doctors
例如,我有如下PlayerType
:
public class PlayerType : ObjectGraphType<Player>
{
public PlayerType(ISkaterStatisticRepository skaterStatisticRepository)
{
Field(x => x.Id);
Field(x => x.Name, true);
Field(x => x.BirthPlace);
Field(x => x.Height);
Field(x => x.WeightLbs);
Field<StringGraphType>("birthDate", resolve: context => context.Source.BirthDate.ToShortDateString());
Field<ListGraphType<SkaterStatisticType>>("skaterSeasonStats",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context => skaterStatisticRepository.Get(context.Source.Id), description: "Player's skater stats");
}
}
公共类PlayerType:ObjectGraphType
{
公共播放器类型(ISkaterStatisticRepository skaterStatisticRepository)
{
字段(x=>x.Id);
字段(x=>x.Name,true);
字段(x=>x.出生地);
字段(x=>x.Height);
字段(x=>x.WeightLbs);
字段(“生日”,解析:上下文=>
public class RootQuery : EfObjectGraphType
{
public RootQuery(IEfGraphQLService efGraphQlService, ISpecializationGraphQlServices specializationServices,
IDoctorGraphQlServices doctorServices, ScheduleDbContext dbContext) : base(efGraphQlService)
{
Name = "Query";
FieldAsync<ListGraphType<SpecializationType>>("specializations"
, resolve: async ctx => {
var selectedFields = GraphQLResolverContextHelpers.GetFirstLevelLeavesNamesPascalCase(ctx.SubFields);
var expression = BuildLinqSelectorObject.DynamicSelectGenerator<Specializations>(selectedFields.ToArray());
return await specializationServices.ListAsync(selector: expression);
});
}
}
public class SpecializationType : EfObjectGraphType<Specializations>
{
public SpecializationType(IEfGraphQLService graphQlService
, IDataLoaderContextAccessor accessor, IDoctorGraphQlServices doctorServices)
: base(graphQlService)
{
Field(p => p.SpecializationId);
Field(p => p.Code);
Field(p => p.SpecializationName);
Field<ListGraphType<DoctorType>, IEnumerable<Doctors>>()
.Name("doctors")
.ResolveAsync(ctx =>
{
var selectedFields = GraphQLResolverContextHelpers.GetFirstLevelLeavesNamesPascalCase(ctx.SubFields);
selectedFields = GraphQLResolverContextHelpers.AppendParrentNodeToEachItem(selectedFields, parentNode: "Doctor");
selectedFields = selectedFields.Union(new[] { "Specializations_SpecializationId" });
var expression = BuildLinqSelectorObject.BuildSelector<SpecializationsDoctors, SpecializationsDoctors>(selectedFields);
var doctorsLoader = accessor.Context
.GetOrAddCollectionBatchLoader<int, Doctors>(
"GetDoctorsBySpecializationId"
, (collection, token) =>
{
return doctorServices.GetDoctorsBySpecializationIdAsync(collection, token, expression);
});
return doctorsLoader.LoadAsync(ctx.Source.SpecializationId);
});
}
}
public class DoctorGraphQlServices : IDoctorGraphQlServices
{
public ScheduleDbContext _dbContext { get; set; }
public DoctorGraphQlServices(ScheduleDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<Doctors>> ListAsync(int? specializationId = null)
{
var doctors = _dbContext.Doctors.AsQueryable();
if(specializationId != null)
{
doctors = doctors.Where(d => d.Specializations.Any(s => s.Specializations_SpecializationId == specializationId));
}
return await doctors.ToListAsync();
}
public async Task<ILookup<int, Doctors>> GetDoctorsBySpecializationIdAsync(IEnumerable<int> specializationIds, CancellationToken token, Expression<Func<SpecializationsDoctors, SpecializationsDoctors>> selector = null)
{
var doctors = await _dbContext.SpecializationsDoctors
.Include(s => s.Doctor)
.Where(spDocs => specializationIds.Any(sp => sp == spDocs.Specializations_SpecializationId))
.Select(selector: selector)
.ToListAsync();
return doctors.ToLookup(i => i.Specializations_SpecializationId, i => i.Doctor);
}
}
public class SpeciaizationGraphQlServices : ISpecializationGraphQlServices
{
public ScheduleDbContext _dbContext { get; set; }
public SpeciaizationGraphQlServices(ScheduleDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<dynamic> ListAsync(string doctorId = null, Expression<Func<Specializations, Specializations>> selector = null)
{
var specializations = _dbContext.Specializations.AsQueryable();
if (!string.IsNullOrEmpty(doctorId))
{
specializations = specializations.Where(s => s.Doctors.Any(d => d.Doctors_Id == doctorId));
}
return await specializations.Select(selector).ToListAsync();
}
public async Task<ILookup<string, Specializations>> GetSpecializationsByDoctorIdAsync(IEnumerable<string> doctorIds, CancellationToken token)
{
var specializations = await _dbContext.SpecializationsDoctors
.Include(s => s.Specialization)
.Where(spDocs => doctorIds.Any(sp => sp == spDocs.Doctors_Id))
.ToListAsync();
return specializations.ToLookup(i => i.Doctors_Id, i => i.Specialization);
}
public IQueryable<Specializations> List(string doctorId = null)
{
var specializations = _dbContext.Specializations.AsQueryable();
if (!string.IsNullOrEmpty(doctorId))
{
specializations = specializations.Where(s => s.Doctors.Any(d => d.Doctors_Id == doctorId));
}
return specializations;
}
}
public class PlayerType : ObjectGraphType<Player>
{
public PlayerType(ISkaterStatisticRepository skaterStatisticRepository)
{
Field(x => x.Id);
Field(x => x.Name, true);
Field(x => x.BirthPlace);
Field(x => x.Height);
Field(x => x.WeightLbs);
Field<StringGraphType>("birthDate", resolve: context => context.Source.BirthDate.ToShortDateString());
Field<ListGraphType<SkaterStatisticType>>("skaterSeasonStats",
arguments: new QueryArguments(new QueryArgument<IntGraphType> { Name = "id" }),
resolve: context => skaterStatisticRepository.Get(context.Source.Id), description: "Player's skater stats");
}
}
public class NHLStatsQuery : ObjectGraphType
{
public NHLStatsQuery(IPlayerRepository playerRepository, NHLStatsContext dbContext)
{
Field<ListGraphType<PlayerType>>(
"players",
resolve: context => {
return dbContext.Players.Select(p =>new Player { Id = p.Id, Name = p.Name });
//return playerRepository.All();
});
}
}