在我的ASP.NET Core 3.1项目中,我使用了CQRS模式。
我已经列出了所有带有查询参数的项目。对于限制和偏移,它是工作的,但对于其他属性,它抛出服务器错误。
我的list方法是这样的。
public class List
{
public class ProjectList
{
public List<ProjectForListDto> Projects { get; set; }
}
public class Query : IRequest<ProjectList>
{
public Query(int? limit, int? offset, string organizationName, int? organizationId, string status)
{
Limit = limit;
Offset = offset;
OrganizationName = organizationName;
OrganizationId = organizationId;
Status = status;
}
public int? Limit { get; set; }
public int? Offset { get; set; }
public string OrganizationName { get; set; }
public int? OrganizationId { get; set; }
public string Status { get; set; }
}
public class Handler : IRequestHandler<Query, ProjectList>
{
private readonly DataContext _context;
private readonly IMapper _mapper;
public Handler(DataContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<ProjectList> Handle(Query request, CancellationToken cancellationToken)
{
var queryable = _context.Projects
.AsQueryable();
if (!string.IsNullOrEmpty(request.OrganizationName) ||
request.OrganizationId > 0 ||
!string.IsNullOrEmpty(request.Status))
{
queryable = queryable.Where(x =>
x.Organization.Name == request.OrganizationName ||
x.OrganizationId == request.OrganizationId || x.Status.ToString("G")
== request.Status);
}
var projects = await queryable.Skip(request.Offset ?? 0)
.Take(request.Limit ?? 50)
.ToListAsync(cancellationToken: cancellationToken);
return new ProjectList
{
Projects = _mapper.Map<List<Project>, List<ProjectForListDto>>(projects)
};
}
}
}
我的控制器动作:
[HttpGet]
public async Task<ActionResult<List.ProjectList>> List(int? offset, int? limit,
string organizationName, int? organizationId, string status) => await
Mediator.Send(new List.Query(limit, offset, organizationName, organizationId, status));
默认情况下,没有任何参数,它返回所有项目,这是确定的,我也可以设置偏移量和限制,但对于场景中的 organizationId,状态和 organizationName,它失败了。
fail: API.Middleware.ErrorHandlingMiddleware[0]
SERVER ERROR
System.InvalidOperationException: The LINQ expression 'DbSet<Project>
.Join(
outer: DbSet<Organization>,
inner: p => EF.Property<Nullable<int>>(p, "OrganizationId"),
outerKeySelector: o => EF.Property<Nullable<int>>(o, "Id"),
innerKeySelector: (o, i) => new TransparentIdentifier<Project, Organization>(
Outer = o,
Inner = i
))
.Where(p => p.Inner.Name == __request_OrganizationName_0 || (Nullable<int>)p.Outer.OrganizationId == __request_OrganizationId_1 || p.Outer.Status.ToString("G") == __request_Status_2)' 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 either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
EF核心不知道如何正确地做GROUP JOIN的在这个时候。我不得不在执行完查询后,在我的代码中对返回的结果集手动执行组连接。
在我写的一个天气预报示例中,我不得不做如下操作来执行一个组连接。
var locationForecasts = await locations.Join(inner: forecasts,
outerKeySelector: location => location.Id,
innerKeySelector: forecast => forecast.LocationId,
resultSelector: (location, forecast) =>
new
{
location,
forecast
}).ToListAsync();
var result = locationForecasts.GroupBy(x => x.location.Id)
.Select(x => new WeatherForecast
{
LocationId = x.Key,
LocationName = x.FirstOrDefault().location.LocationName,
Forecasts = x.Select(y => y.forecast)
}).FirstOrDefault();
这里的重点是在SQL中执行一个JOIN,然后返回结果,然后在应用层执行GroupBy。
希望EF能尽快支持这一点,但与此同时,这是我一直在使用的变通方法。反正物质化的SQL查询似乎会吐出相同的Join和GroupJoin的代码,所以我不相信这是一个性能问题。