Skip to content
This library automaticly generates LINQ Expressions from Dtos to Database Entities
C# Dockerfile
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
content
sandbox/WebApplication.API
src/AutoFilterer
tests/AutoFilterer.Tests
.dockerignore
.gitattributes
.gitignore
AutoFilterer.sln
LICENSE
README.md
azure-pipelines.yml

README.md

Logo

AutoFilterer

This project aims to create filtered endpoint without writing any of query with entity framework. Just prepare your filter model and apply it into your Db Entity.

Basicly provides QueryString To Linq conversion.

You can visit Wiki for more documents

Nuget WiKi CodeFactor Build status

Getting Started

  • Install AutoFilterer NuGet package from here.

That's it. You don't need any initialization


Usage

Just visit WiKi for better understanding of usage.

Basics

  • Let say you have a Model like that:
public class Blog
{
    public string BlogId { get; set; }
    public int CategoryId { get; set; }
    public int Priority { get; set; }
    public bool IsPublished { get; set; }
    public DateTime PublishDate { get; set; }
}
  • Let's create a filtering DTO like that:
public class BlogFilterDto : FilterBase<Blog>
{
    public int CategoryId { get; set; }
    public int Priority { get; set; }
    public bool? IsPublished { get; set; }
}
  • Let's create a sample Controller and get the DTO from querystring
using AutoFilterer.Extensions; // <-- To call extension methods
//...
public class BlogsController : ControllerBase
{
    [HttpGet]
    public IActionResult Get([FromQuery]BlogFilterDto filter)
    {
        using(var db = new MyDbContext())
        {
            var blogList = db.Blogs.ApplyFilter(filter);
            return Ok(blogList);
        }
    }
}
  • Just send following requests to check result:

    • /Blogs?IsPublished=False
    • /Blogs?CategoryId=4
    • /Blogs?Priority=4
    • /Blogs?IsPublished=True&Priority=1
    • /Blogs?IsPublished=True&Priority=5&CategoryId=1

Using string comparisons

  • Let's add a string column to model:
public class Blog
{
    public string BlogId { get; set; }
    public int CategoryId { get; set; }
    public string Title { get; set; } // <-- We'll work on this string field
    public int Priority { get; set; }
    public bool IsPublished { get; set; }
    public DateTime PublishDate { get; set; }
}
  • And of course add DTO too
public class BlogFilterDto : FilterBase<Blog>
{
    public int? CategoryId { get; set; }
    public int? Priority { get; set; }
    public string Title { get; set; } // <-- Same property name with Entity's property
    public bool? IsPublished { get; set; }
}
  • Let's add StringFilterOptions attribute on the string field to search as Contains instead of exact value:
public class BlogFilterDto : FilterBase<Blog>
{
    public int? CategoryId { get; set; }
    public int? Priority { get; set; }
    [StringFilterOptionsAttribute(StringFilterOption.Contains)]
    public string Title { get; set; }
    public bool? IsPublished { get; set; }
}
  • You can send following requests to check result. That's awesome!

    • /Blogs?Title=Hello
    • /Blogs?Title=Hello%20World
    • /Blogs?Title=a

Working With Range

You may want to search a range like bwetween two DateTimes or numbers.

  • The Model is same with previous sample:
public class Blog
{
    public string BlogId { get; set; }
    public int CategoryId { get; set; }
    public string Title { get; set; }
    public int Priority { get; set; }
    public bool IsPublished { get; set; }
    public DateTime PublishDate { get; set; }
}
  • Just use following Range<T> type in your DTO: WARNING: Do not use nullable types as generic type parameter in Range<T>. Use same type with your entity. If your property is already nullable in entity, it's ok. Just use same type with model with Range
public class BlogFilterDto : FilterBase<Blog>
{
    public int? CategoryId { get; set; }
    public Range<int> Priority { get; set; } // <-- Careful! Do not use nullable Types as Generic Type parameter
    public string Title { get; set; }
    public bool? IsPublished { get; set; }

    public Range<DateTime> PublishDate { get; set; } // <-- Use Range<DateTime> instead of below
    //public DateTime PublishDate { get; set; }
}
  • Now, querystring is changed a little bit. Try following requests:

    • /Blogs?Priority.Min=4
    • /Blogs?PublishDate.Max=01.05.2019 // Depends on CultureInfo. If you're using Request Localization, each client must send by its own datetime format_
    • /Blogs?Priority.Min=3&Priority.Max=5

Working with Pagination

  • Just inherit your Dto from PaginationFilter instead of FilterBase:
public class BlogFilterDto : PaginationFilterBase<Blog> // <-- That's all!
{
    public int? CategoryId { get; set; }
    public Range<int> Priority { get; set; }
    public string Title { get; set; }
    public bool? IsPublished { get; set; }
    public Range<DateTime> PublishDate { get; set; }
}
  • And One more thing is required to apply paginations: Ordering By
public class BlogsController : ControllerBase
{
    [HttpGet]
    public IActionResult Get([FromQuery]BlogFilterDto filter)
    {
        using(var db = new MyDbContext())
        {
            // You just need to apply an ordering before calling ApplyFilter() method
            var blogList = db.Blogs.OrderBy(o => o.PublishDate).ApplyFilter(filter);
            return Ok(blogList);
        }
    }
}
  • You can use page and perPage querystring parameters with all other parameters:
    • /Blogs?page=2
    • /Blogs?perPage=10
    • /Blogs?perPage=10&page=2
    • /Blogs?perPage=2&page=3&priority.min=3&title=h

Customizations

Customizing QueryString

You may want to use shorter parameter names in querystring. AspNetCore provides to define querystring parameter's names with FromQuery attribute:

public class BlogFilterDto : FilterBase<Blog>
{
    [FromQuery(Name="category")]   // < -- This attribute may be used to customize your querystring
    public int CategoryId { get; set; }// <--  But property name must be same with entity
    public int Priority { get; set; }
    public bool? IsPublished { get; set; }
}

This provides to handle category parameter as CategoryId:

* `/Blogs?category=4`

Customizing Pagination Parameters

You may want to use different names instead of page and perPage

  • Just go your Filter Dto and override them:
public class BlogFilterDto : PaginationFilterBase<Blog>
{
    public int? CategoryId { get; set; }
    public Range<int> Priority { get; set; }
    public string Title { get; set; }
    public bool? IsPublished { get; set; }
    public Range<DateTime> PublishDate { get; set; }

    [FromQuery(Name = "p")] // <-- you can set querystring name
    public override int Page { get => base.Page; set => base.Page = value; }

    [FromQuery(Name = "limit")]
    public override int PerPage { get => base.PerPage; set => base.PerPage = value; }
}
  • You can change default values also:
    public class BlogFilterDto : PaginationFilterBase<Blog>
    {
        public BlogFilterDto()
        {
            base.PerPage = 32; // Sets default value when object is initialized.
            // Model binder will set property after constructor if request has this parameter.
        }
        public int? CategoryId { get; set; }

        public Range<int> Priority { get; set; }

        [StringFilterOptions(StringFilterOption.Contains)]
        public string Title { get; set; }

        public bool? IsPublished { get; set; }

        public Range<DateTime> PublishDate { get; set; }
    }

Swagger

Of course swagger will see your parameters and use them. 👍

You can’t perform that action at this time.