/// <summary> /// Combine multiple expressions using Or. Any null expressions are skipped. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="expressions"></param> /// <returns></returns> public static Expression <Func <T, bool> > CombineOr <T>(params Expression <Func <T, bool> >[] expressions) { Expression <Func <T, bool> > ret = null; foreach (var expression in expressions) { if (expression == null) { continue; } if (ret == null) { ret = expression; } else { ret = WhereExpressionUtilities.Or(ret, expression); } } return(ret); }
/// <summary> /// Function that actually executes as the data loader. /// </summary> /// <typeparam name="Model"></typeparam> /// <param name="requests"></param> /// <returns></returns> protected virtual async Task <Dictionary <DataLoaderRequest <Model, PrimaryKey>, IEnumerable <Model> > > LoadData <Model, PrimaryKey>(IEnumerable <DataLoaderRequest <Model, PrimaryKey> > requests) where Model : class { using var scope = _serviceProvider.CreateScope(); var repository = scope.ServiceProvider.GetService <IRepository <Model, PrimaryKey> >(); // If we only have one request to handle, we can do everyting on the server without any fancy processing, so handle that case now. // (Unless we are using a feature not supported by the repository API (e.g. ThenBy), in which case we still treat it as a batch). if (requests.Count() == 1) { var request = requests.First(); bool needCodeSideOrdering = request.ThenBy != null || request.OrderByDescending; if (!needCodeSideOrdering) { var singleResults = await repository.FindAllAsync(request.Where, request.OrderBy, request.Skip, request.Take); return(requests.ToDictionary( item => item, item => singleResults )); } else { var singleResults = await repository.FindAllAsync(request.Where, request.OrderBy); var batchResult = singleResults; if (request.OrderBy != null) { IOrderedEnumerable <Model> ordered; if (request.OrderByDescending) { ordered = batchResult .OrderByDescending(request.OrderBy.Compile()); } else { ordered = batchResult .OrderBy(request.OrderBy.Compile()); } if (request.ThenBy != null) { if (request.ThenByDescending) { ordered = ordered .ThenByDescending(request.ThenBy.Compile()); } else { ordered = ordered .ThenBy(request.ThenBy.Compile()); } } batchResult = ordered; } if (request.Skip != 0) { batchResult = batchResult.Skip(request.Skip); } if (request.Take.HasValue) { batchResult = batchResult.Take(request.Take.Value); } return(requests.ToDictionary( item => item, item => batchResult )); } } // Otherwise we need to read everything we require and then apply the order, skip, and take after getting the data. // // Build a combined where clause. Expression <Func <Model, bool> > whereAny = null; foreach (var request in requests) { if (whereAny == null) { whereAny = request.Where; } else { whereAny = WhereExpressionUtilities.Or(whereAny, request.Where); } } // Read all results for all batched requests. var results = await repository.FindAllAsync(whereAny); // Split the results back out by their requests, applying the order, skip, and take. var ret = requests.ToDictionary( item => item, item => { var batchResult = results.Where(item.Where.Compile()); if (item.OrderBy != null) { IOrderedEnumerable <Model> ordered; if (item.OrderByDescending) { ordered = batchResult .OrderByDescending(item.OrderBy.Compile()); } else { ordered = batchResult .OrderBy(item.OrderBy.Compile()); } if (item.ThenBy != null) { if (item.ThenByDescending) { ordered = ordered .ThenByDescending(item.ThenBy.Compile()); } else { ordered = ordered .ThenBy(item.ThenBy.Compile()); } } batchResult = ordered; } if (item.Skip != 0) { batchResult = batchResult.Skip(item.Skip); } if (item.Take.HasValue) { batchResult = batchResult.Take(item.Take.Value); } return(batchResult); } ); return(ret); }