ASP.NET Core WebAPI? You should start with Panner.AspNetCore!
Clean, customizable & extensible framework for the configuration, parsing and application of sorts and filters from a CSV.
Setting up your context to define your entities and rules.
var builder = new PContext();
// Entity wide configuration (Option 1)
builder.Entity<Post>()
.AllPropertiesAreSortableByName();
.AllPropertiesAreFilterableByName();
// A more granular approach (Option 2)
builder.Entity<Post>()
.Property(x => x.Id, o => o
.IsSortableByName()
.IsFilterableByName()
).Property(x => x.Title, o => o
.IsSortableByName()
.IsFilterableByName()
).Property(x => x.CreatedOn, o => o
.IsSortableAs("Creation")
.IsFilterableAs("Creation")
);
// And then we build our context.
IPContext context = builder.Build();
// Ta-daah! Now we have a context!
Feeding Panner's context our CSV inputs and obtaining sort/filter particles
var csvSort = "-Title, Id"; // Example
if (!context.TryParseCsv(
input: csvSort,
out IEnumerable<ISortParticle<TEntity>> sortParticles
)){
// Parsing failed
return;
}
// Ta-daah! Now we have sort particles!
var csvFilter = "Id<666, Id>13"; // Example
if (!context.TryParseCsv(
input: csvFilter,
out IEnumerable<IFilterParticle<TEntity>> filterParticles
)){
// Parsing failed
return;
}
// Ta-daah! Now we have filter particles!
Using Panner's extension methods to apply our particles to any IEnumerable
, IQueryable
, DbSet
, etc..
var pannedQueryable = posts
.Apply(filterParticles)
.Apply(sortParticles);
// Ta-daah! Now we have a filtered and sorted IQueryable!
Custom sort for a specific entity
Ability to sort our fake entity Post
by "popularity", an arbritary concept and not a property of the entity.
Leveraging ISortParticle
, ISortParticleGenerator
and extending PEntityBuilder
.
public class SortPostByPopularityParticle : ISortParticle<Post> // Panner's interface
{
readonly bool Descending;
public SortPostByPopularityParticle(bool descending)
{
this.Descending = descending;
}
public IOrderedQueryable<Post> ApplyTo(IOrderedQueryable<Post> source)
{
// Here's how the sorting is done when the particle is applied.
if (this.Descending)
return source
.ThenByDescending(x => x.AmtLikes)
.ThenByDescending(x => x.AmtComments);
else
return source
.ThenBy(x => x.AmtLikes)
.ThenBy(x => x.AmtComments);
}
}
public class SortPostsByPopularityParticleGenerator : ISortParticleGenerator<Post> // Panner's interface
{
public bool TryGenerate(IPContext context, string input, out ISortParticle<Post> particle)
{
var descending = input.StartsWith('-');
var remaining = descending ? input.Substring(1) : input;
if (!remaining.Trim().Equals("Popularity", System.StringComparison.OrdinalIgnoreCase))
{
// Not the input we're interested in.
particle = null;
return false;
}
particle = new SortPostByPopularityParticle(descending);
return true;
}
}
public static partial class PEntityBuilderExtensions
{
/// <summary>Marks the entity as sortable by popularity.</summary>
public static PEntityBuilder<Post> IsSortableByPopularity(this PEntityBuilder<Post> builder)
{
builder.GetOrCreateGenerator<ISortParticle<Post>, SortPostsByPopularityParticleGenerator>();
return builder; // So we can chain calls!
}
}
builder.Entity<Post>()
.IsSortableByPopularity();
Using different culture for filter value conversions?
You can change CurrentInfo.CurrentCulture
before calling context.TryParseCsv([...])
!
CurrentInfo.CultureInfo = new CultureInfo("es"); //So simple...!