Reputation:
I know the title is not good.
I have a service that contain all of my entity functions.
In my service there is a function with this structure
IList<T> GetAllPaged<TKey>(
List<Expression<Func<T, bool>>> predicate,
Expression<Func<T, TKey>> orderKeySelector
);
This function gets data from one entity and orders it.
Now I want to do more.
First to select from one entity then group it in order it and at the end select new items from grouped items.
Something like this:
IList<TReturn> GetAllPaged<TResult, TKey, TGroup, TReturn>(
List<Expression<Func<T, bool>>> predicate,
Expression<Func<T, TResult>> firstSelector,
Expression<Func<TResult, TKey>> orderSelector,
Func<TResult, TGroup> groupSelector,
Func<IGrouping<TGroup, TResult>, TReturn> selector
);
Is that possible?
Upvotes: 3
Views: 3167
Reputation: 17146
Here is a solution to what I think you asked for.
First, the base repository. I've created a base repository because I hope you will not expose this madness to the application but to the repositories only.
abstract class BaseRepo<TEntity>
{
// If you're using Entity Framework, this method could
// be implemented here instead.
protected abstract IQueryable<TEntity> Entities { get; }
protected IList<TReturn> GetAllPaged<TResult, TKey, TGroup, TReturn>(
List<Expression<Func<TEntity, bool>>> predicates,
Expression<Func<TEntity, TResult>> firstSelector,
Expression<Func<TResult, TKey>> orderSelector,
Func<TResult, TGroup> groupSelector,
Func<IGrouping<TGroup, TResult>, TReturn> selector)
{
return predicates
.Aggregate(Entities, (current, predicate) => current.Where(predicate))
.Select(firstSelector)
.OrderBy(orderSelector)
.GroupBy(groupSelector)
.Select(selector)
.ToList();
}
}
Then the implementation.
class HorseRepo : BaseRepo<Horse>
{
// This will of course be some data source
protected override IQueryable<Horse> Entities
{
get
{
return new List<Horse> {
new Horse { Id = 1, Name = "Mr Horse", Color = "Brown" },
new Horse { Id = 2, Name = "Mrs Horse", Color = "White" },
new Horse { Id = 3, Name = "Jr Horse", Color = "White" },
new Horse { Id = 4, Name = "Sr Horse", Color = "Black" },
new Horse { Id = 5, Name = "Dr Horse", Color = "Brown" },
}.AsQueryable();
}
}
// This is what I think you should expose to the application
// This is the usage of the expression fest above.
public IEnumerable<GroupedHorses> GetGroupedByColor() {
return horseRepo.GetAllPaged(
new List<Expression<Func<Horse, bool>>> {
h => h.Name != string.Empty,
h => h.Id > 0
},
h => new HorseShape { Id = h.Id, Name = h.Name, Color = h.Color },
hs => hs.Name,
hs => hs.Color,
g => new GroupedHorses
{
Color = g.Key,
Horses = g.ToList()
}
);
}
}
Some classes needed:
class GroupedHorses
{
public string Color { get; set; }
public IList<HorseShape> Horses { get; set; }
}
class HorseShape
{
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
}
// Define other methods and classes here
class Horse
{
public int Id { get; set; }
public string Name { get; set; }
public string Color { get; set; }
public string UninterestingProperty { get; set; }
}
Upvotes: 2
Reputation: 9294
Maybe you can pass a generic function that does something with your entities? I know Expressions are fun, but you should always consider if the end result is better than something simpler.
Something like this:
TResult Query<TEntity, TResult>(Func<IQueryable<TEntity>, TResult> queryFunction)
Implementation for Entity Framework:
using(var context = new SomeContext())
{
return queryFunction(context.Set<TEntity>());
}
Usage:
var listOfCars = carService.Query(cars => cars
.Select(c => new { Id = c.Id, Name = c.Name }) // c stands for car
.GroupBy(a => a.Name) // a stands for "anonymous object"
.OrderBy( /* order your group in some fashion */)
.ToList()
);
If you put all this logic in separate parameters, you will lose the ability to use anonymous objects and you will find yourself writing a lot of service methods.
Upvotes: 1