FilterBuilder 1.1.3
See the version list below for details.
dotnet add package FilterBuilder --version 1.1.3
NuGet\Install-Package FilterBuilder -Version 1.1.3
<PackageReference Include="FilterBuilder" Version="1.1.3" />
paket add FilterBuilder --version 1.1.3
#r "nuget: FilterBuilder, 1.1.3"
// Install FilterBuilder as a Cake Addin #addin nuget:?package=FilterBuilder&version=1.1.3 // Install FilterBuilder as a Cake Tool #tool nuget:?package=FilterBuilder&version=1.1.3
Filter Builder Documentation
Introduction
Filter Builder is an ASP.NET library designed to simplify the creation and management of dynamic query filters using LINQ. It is ideal for applications requiring complex search functionalities with dynamic user inputs, allowing seamless integration across various data sources.
Features
- Dynamic Filter Creation: Enables the creation of LINQ expressions for dynamic query generation directly from user inputs.
- Flexibility with Data Sources: Can be used with any data source compatible with LINQ, enhancing its versatility.
- Easy Integration: Easily integrates into existing projects with minimal setup.
Getting Started
Prerequisites
- .NET Core 3.1 or later
- Visual Studio 2019 or later
Installation
Before you can use the Filter Builder, you need to install it along with its dependencies via NuGet. Here are the commands to install each package:
Akinzekeel.ExpressionBuilder: This package helps in building complex LINQ expressions dynamically:
Install-Package Akinzekeel.ExpressionBuilder
Microsoft.EntityFrameworkCore.DynamicLinq: This package extends Entity Framework capabilities to support dynamic LINQ operations:
Install-Package Microsoft.EntityFrameworkCore.DynamicLinq
Newtonsoft.Json: A popular high-performance JSON framework for .NET:
Install-Package Newtonsoft.Json
Microsoft.AspNetCore.Mvc.NewtonsoftJson: Adds Newtonsoft.Json support to ASP.NET Core MVC:
Install-Package Microsoft.AspNetCore.Mvc.NewtonsoftJson
Ensure all these packages are installed to make full use of the Filter Builder in your projects.
Configuration
After installing the necessary packages, you need to configure your ASP.NET Core application to use Newtonsoft.Json
for handling JSON operations. This is done in the Program.cs
file of your project. Add the following line of code in the Program.cs
to set up Newtonsoft.Json
:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers().AddNewtonsoftJson();
Creating Filters with Filter Builder
The Filter Builder library provides a powerful and flexible way to build dynamic query filters based on your model's properties. Understanding how to specify these filters is crucial for effective use.
Key Expression Syntax (KeyExp
)
The library uses a specific convention for referencing properties in the filter criteria, known as KeyExp
. Here's how to address different types of properties:
- Direct Properties: Reference any direct value property by its name.
- Example:
Id
,Name
,Gender
- Example:
- Nested Properties: For properties within a reference type, use dot notation.
- Example:
Address.City
,Employer.Name
- Example:
- List Properties: To filter based on properties of items within a list, use the property name followed by the item's property in brackets.
- Example:
Contacts[Type]
,Contacts[Value]
- Example:
Supported Operations
The operations available are grouped by the type of data they are meant to work with, ensuring that you can apply relevant filters based on the property type:
Default Operations
Equal
NotEqual
Text Operations
Contains
DoesNotContain
EndsWith
IsEmpty
IsNotEmpty
IsNotNull
IsNotNullNorWhiteSpace
IsNull
IsNullOrWhiteSpace
NotEqual
StartsWith
Number Operations
Between
Equal
GreaterThan
GreaterThanOrEqual
LessThan
LessThanOrEqual
NotEqual
Boolean Operations
Equal
NotEqual
Date Operations
Between
Equal
GreaterThan
GreaterThanOrEqual
LessThan
LessThanOrEqual
NotEqual
Connectors
And
: Used to combine multiple conditions where all conditions must be true for the combined result to be true. This is the primary logical connector used to ensure that all specified conditions are met.Or
: Used to combine multiple conditions where at least one of the conditions must be true for the combined result to be true. This connector allows for more flexibility by satisfying any one of the multiple specified conditions.
Specifying Values
Value
: Specify the primary value for the operation.Value2
: Used for operations likeBetween
to specify the secondary value.
Filter Usage
Here is how you would build a filter using the Filter Builder in an ASP.NET application:
Using Filter Builder in Code with Defined Class Properties
To effectively use the Filter Builder directly in your code, you should define a class with properties that match the names and types of the entity properties you want to filter. This ensures that the filter conditions you set up will correctly map to the corresponding fields in your database.
Step 1: Defining the Model Class
First, define a class that represents the model or database entity you are working with.
// Entity Model public class User { public int Age { get; set; } public bool IsActive { get; set; } public string Name { get; set; } public BirthData Birth { get; set; } public List<Contact> Contacts { get; private set; } } public class BirthData { public DateTime Date { get; set; } public string Country { get; set; } } public class Contact { public string Value { get; set; } public string Comments { get; set; } }
Step 2: Using the Filter Class to Create a Filter DTO
Next, define a DTO (Data Transfer Object) that utilizes the Filter class for each property you might want to filter on within the User class, And ensure that property names and types in this class exactly match those in your database entity.
public class UserFilterDTO { // Using default filter public int Age { get; set; } public bool IsActive { get; set; } // Using Advance filter public Filter<string> Name { get; set; } public Filter<string> Birth { get; set; } public Filter<string> Contact { get; set; } }
Step 3: Filtering Entities Using the Filter DTO
With the
UserFilterDTO
set up, you can now build filter expressions based on this DTO and apply them to filter data in your application.// Instantiate the filter DTO and set filter conditions var filterDto = new UserFilterDTO { //Equals Default logical operation if not set with all type except string and date Age = 19, IsActive = true, Name = new Filter<string> { KeyExp = "Name", // Default key expression if not set Value = "Moh", Operation = "StartsWith", Connector="And" // Default logical connector if not set }, BirthCountry = new Filter<string> { KeyExp = "Birth.Country", // This will filter by the country of the birth date Value = "USA", Operation = "Equal" }, ContactValue = new Filter<string> { KeyExp = "Contacts[Value]", // This will filter by each contact's value in the contacts list Value = "@example.com", Operation = "Contains" // Default logical connector if not set } }; // Use the filter DTO to build a filter expression and fetch filtered results var filterExpression = FilterExpression.Build<User, UserFilterDTO>(filterDto); var filteredUsers = dbContext.Users.Where(filterExpression).ToList(); return filteredUsers;
Implementing Filter Requests in API Controllers
Integrating advanced filtering into an API involves receiving filter parameters through request models. Below is a guide on setting up your API controller to handle complex filters using the UserFilterDTO and how to manage JSON serialization issues by extending a base class.
Step 1: Extend UserFilterDTO from FilterSearch
Modify UserFilterDTO to inherit from FilterSearch to utilize advanced filtering capabilities and manage JSON serialization effectively.
public class UserFilterDTO : FilterSearch { public int Age { get; set; } public bool IsActive { get; set; } public string Name { get; set; } public string Birth { get; set; } public string Contact { get; set; } }
Step 2: API Controller Setup
Set up an API controller that includes an action method to receive the UserFilterDTO. This method will use the model to filter data based on the provided criteria.
[ApiController] [Route("[controller]")] public class UsersController : ControllerBase { private readonly DbContext dbContext; public UsersController(DbContext dbContext) { this.dbContext = dbContext; } [HttpPost("filter")] public ActionResult<List<User>> FilterUsers([FromBody] UserFilterDTO filterDto) { var filterExpression = FilterExpression.Build<User, UserFilterDTO>(filterDto); var filteredUsers = dbContext.Users.Where(filterExpression).ToList(); return Ok(filteredUsers); } }
Sending a Filter Request Using Body Form
To send a request to the API with the filter contained in the body of the request, you can use tools like Postman, or write a client-side script using fetch API or similar libraries.
Example Enter the JSON Body
{ "Age": 19, "IsActive": true, "Name": {"Value": "John", "Operation": "StartsWith"}, // When using date type or value2, Between is defult logic operation "Birth": {"KeyExp":"Birth.Date","Value": "1990-01-01", "Value2":"1994-01-01", "Operation": "Between"}, "Contact": {"Value": "info@example.com", "Operation": "Contains"}, }
Setting Custom Null Values for Filters
To handle custom null values for different data types in your filters, you can create a class that initializes these values and then inherit this class from
FilterSearch
.Define the FilterSearchWithNullableValue Class This class will extend
FilterSearch
and set default null values for various types.public class FilterSearchWithNullableValue : FilterSearch { public FilterSearchWithNullableValue() { NullValue = new List<(Type, object)> { (typeof(int), 0), (typeof(long), 0), (typeof(float), 0), (typeof(double), 0), (typeof(string), ""), (typeof(DateTime), DateTime.MinValue), (typeof(DateTime?), null) }; } }
Extend
UserFilterDTO
fromFilterSearchWithNullableValue
, allowing it to use the custom null values defined.public class UserFilterDTO : FilterSearchWithNullableValue { public int Age { get; set; } public bool IsActive { get; set; } public string Name { get; set; } public string Birth { get; set; } public string Contact { get; set; } }
Overloads of FilterExpression.Build
The FilterExpression.Build
method has several overloads to accommodate different filtering needs. Below are the available overloads and their descriptions, followed by examples for each.
1. Build<TEntity, TFilter>(TFilter filter)
Creates a filter expression based on a strongly-typed filter DTO.
Parameters:
filter
: A strongly-typed DTO containing the filtering criteria.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the filter.
Example:
var filterDto = new UserFilterDTO
{
Name = new Filter<string> { Value = "Ahmed", Operation = "Equals" }
};
var filterExpression = FilterExpression.Build<User, UserFilterDTO>(filterDto);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
2. Build<TEntity, TFilter>(TFilter[] filters, params string[] stringExpressions)
Combines multiple filter objects and string expressions into a single filter expression.
Parameters:
filter
: A strongly-typed DTO containing the filtering criteria.stringExpressions
: Additional filter criteria as string expressions.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the combined filter.
Example:
var filterDto1 = new UserFilterDTO { Age = 19 };
var filterDto2 = new UserFilterDTO { IsActive =true };
var filters = new[] { filterDto1, filterDto2 };
var stringExpressions = new[] { "e => e.Name == \"Mohamed\"" };
var filterExpression = FilterExpression.Build<User, UserFilterDTO>(filters, stringExpressions);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
3. Build<TEntity>(string stringExpression)
Parses a string expression into a LINQ expression.
Parameters:
stringExpressions
: Additional filter criteria as string expressions.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the filter.
Example:
var stringExpression = "x=> x.Age == 25 && x.IsActive == true";
var filterExpression = FilterExpression.Build<User>(stringExpression);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
4. Build<TEntity>(params Expression<Func<TEntity, bool>>[] expressions)
Combines multiple expressions into one by using the AndAlso
logical operator.
Parameters:
expressions
: An array of filter expressions.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the combined filter.
Example:
Expression<Func<User, bool>> expr1 = user => user.Age == 25;
Expression<Func<User, bool>> expr2 = user => user.IsActive;
var filterExpression = FilterExpression.Build(expr1, expr2);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
5. Build<TEntity>(Expression<Func<TEntity, bool>>[] filters, params string[] stringExpressions)
Combines filter expressions and string expressions into a single expression.
Parameters:
filters
: An array of filter expressions.stringExpressions
: Additional filter criteria as string expressions.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the combined filter.
Example:
Expression<Func<User, bool>> expr1 = user => user.Age == 25;
var stringExpressions = new[] { "x=> x.IsActive == true" };
var filterExpression = FilterExpression.Build(new[] { expr1 }, stringExpressions);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
6. Build<TEntity>(Expression<Func<TEntity, bool>> filter, string stringExpression)
Simplifies creating an expression from a single filter and a string expression.
Parameters:
filter
: A filter expression.stringExpression
: A filter criterion as a string expression.
Returns:
- An
Expression<Func<TEntity, bool>>
representing the combined filter.
Example:
Expression<Func<User, bool>> filter = user => user.Age == 25;
var stringExpression = "x => x.IsActive == true";
var filterExpression = FilterExpression.Build(filter, stringExpression);
var filteredUsers = dbContext.Users.Where(filterExpression).ToList();
Extensions and Options
The FilterExpression
class can be extended with additional methods to provide more flexibility and functionality. Below are some useful extension methods for enhancing the capabilities of FilterExpression
.
1. TranslateExpression
Recursively translates an expression to optimize or modify it, making it suitable for caching. This method ensures that the expression can be serialized or used as a cache key in Redis.
Example Usage:
Expression<Func<User, bool>> expr = user => user.Age == 25 && user.IsActive;
var translatedExpr = expr.TranslateExpression();
2. CleanExpression
Cleans an expression to simplify it, making it more suitable for scenarios like Redis caching.
Example Usage:
Expression<Func<User, bool>> expr = user => user.Age == 25 && user.IsActive;
var cleanedExpr = expr.CleanExpression();
3. ApplyOwnership
Modifies an expression to enforce ownership rules based on the provided RequestInfo
.
Example Usage:
var requestInfo = new RequestInfo
{
TableName = "User",
Ownership = "e => (e.UserId == 32)",
UserStores = "1,2,3"
};
Expression<Func<User, bool>> expr = user => user.IsActive;
var ownershipExpr = expr.ApplyOwnership(requestInfo);
Utility Methods and Functions
The UtilityMethods
class provides a set of helper functions that assist with various tasks such as retrieving property values, converting types, and comparing objects. These utility methods are used throughout the FilterExpression and other related classes to facilitate dynamic filtering and expression building.
1. GetObjectValue
Retrieves the value of a specified property from an object.
public static object GetObjectValue(object anonymousObject, string propertyName)
Parameters:
anonymousObject
: The object from which to retrieve the property value.propertyName
: The name of the property to retrieve.
Returns:
- The value of the specified property, or null if not found.
Example:
var user = new { Name = "John", Age = 30 };
var name = UtilityMethods.GetObjectValue(user, "Name"); // Returns "John"
2. AreEqual
Compares two objects for equality, treating them as strings.
public static bool AreEqual(object obj1, object obj2)
Parameters:
obj1
: The first object to compare.obj2
: The second object to compare.
Returns:
- True if the objects are equal; otherwise, false.
Example:
var isEqual = UtilityMethods.AreEqual("hello", "HELLO"); // Returns true
3. ConvertFromObject
Converts an object to a specified target type TTarget
.
public static TTarget ConvertFromObject<TTarget>(object source)
Parameters:
source
: The source object to convert.
Returns:
- The converted object of type
TTarget
, or default if conversion is not possible.
Example:
int number = UtilityMethods.ConvertFromObject<int>("123"); // Returns 123
4. GetDefaultValue
Gets the default value for a specified type.
public static object GetDefaultValue(Type type)
Parameters:
type
: The type for which to get the default value.
Returns:
- The default value for the type.
Example:
var defaultInt = UtilityMethods.GetDefaultValue(typeof(int)); // Returns 0
5. ConvertValueToPropertyType
Converts a value to the type of a specified property.
public static object ConvertValueToPropertyType(PropertyInfo property, object value)
Parameters:
property
: The property whose type will be used for conversion.value
: The value to convert.
Returns:
- The value converted to the property's type, or null if conversion fails.
Example:
PropertyInfo propertyInfo = typeof(User).GetProperty("Age");
var convertedValue = UtilityMethods.ConvertValueToPropertyType(propertyInfo, "25"); // Returns 25 (int)
6. IsInheritedFrom
Checks if a type inherits from a specified base type.
public static bool IsInheritedFrom(Type objectType)
Parameters:
objectType
: The type to check.
Returns:
True if objectType
inherits from the base type; otherwise, false.
Example:
bool isInherited = UtilityMethods.IsInheritedFrom(typeof(MyCustomFilter)); // Checks if MyCustomFilter inherits from FilterSearch
Thank You
Thank you for using the FilterExpression library. If you have any questions, suggestions, or would like to contribute to the project, your feedback and contributions are always welcome!
Happy filtering!
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- Microsoft.CSharp (>= 4.7.0)
- Microsoft.Extensions.Configuration.Binder (>= 2.2.0)
- Microsoft.Extensions.Configuration.Json (>= 2.2.0)
- Newtonsoft.Json (>= 13.0.3)
- System.ComponentModel.Annotations (>= 4.5.0)
- System.Configuration.ConfigurationManager (>= 4.5.0)
- System.Linq.Dynamic.Core (>= 1.4.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
This is a fork of dbelmont/LamdaExpressionBuilder. The fork is aimed towards .Net Core 3.1 and newer which may not work well with the original source code.