-
Notifications
You must be signed in to change notification settings - Fork 1
Building Queries
As stated in introduction, queryable builder is made to simplify querying your datasource using query objects. Based on queryObject properties and their attributes, builder can filter, sort and paginate through your IQueyryable.
To query your datasource, builder uses the combination of predefined attributes, operators and conventions.
Further in text, whenever there is a source
variable, it is of type IQueryable<Product>
where Product
is the following type:
public class Product
{
public decimal Price { get; set; }
public string Name { get; set; }
public string BrandName { get; set; }
}
In most scenarios, you will want to use nullable types as your query object properties. When the property has the value of null
, the builder simply ignores that property - it's not included in the predicate.
Consider the following scenario:
public class ProductQuery
{
[QueryProperty]
public decimal? Price { get; set; }
}
var queryObject = new ProductQuery { Price = null };
var result = source.BuildQuery(queryObject);
The result are going to be IQueryable with no predicate built - queryable with all the products will be returned. However, if we set the price property to some value, only products who'se price matches the value will be returned:
var queryObject = new ProductQuery { Price = 250 };
var result = source.BuildQuery(queryObject);
The query resulted with all the product having the price set exactly to 250.
There is a set of operators, exposed as enum for you to state the resulting predicate operation. They are all part of the ComparisonOperator
enum:
public enum ComparisonOperator
{
MoreThan,
MoreThanOrEqualsTo,
LessThan,
LessThanOrEqualsTo,
Equals,
Contains,
StartsWith,
EndsWith,
NotEqual
}
Each operator tells the builder how to treat the incomming property value against the actual datasource property. By default, if no operator is stated, Equals is used. When the queryObject property is not null, operator is going to dictate the underlying predicate.
public class ProductQuery
{
[QueryProperty(ComparisonOperator.Contains)]
public string Name { get; set; }
}
var queryObject = new ProductQuery { Name = "test" };
Using the query object above, the resulting query will contain all the products containing "test" in their name, regardless of the text-case.
Currently, there are:
- QueryProperty,
- QueryProperties,
- WithQueryProperty
- WithAnyProperty
Includes decorated property to a query building process.
Overloads:
- Default Constructor
public class ProductQuery
{
[QueryProperty]
public string Name { get; set; }
}
var query = new ProductQuery { Name = "a" };
var result = datasource.BuildQuery(query);
QueryProperty setup:
- Operator - Equals
- Property to target - Name
Resulting query will include products filtered by their Name property.
- Parameterized constructor QueryProperty constructor takes up to 2 parameters - operator and propertyName. Property name represents a resulting type property name query is built against.
public class ComplexishProductQuery
{
[QueryProperty(propertyName: "Price")]
public decimal? CustomProp { get; set; }
[QueryProperty(ComparisonOperator.Contains)]
public string Name { get; set; }
[QueryProperty(ComparisonOperator.StartsWith, "BrandName")]
public string CustomProp2 { get; set; }
}
var query = new ComplexishProductQuery
{
CustomProp = 22,
Name = "Fo",
CustomProp2 = "Fa"
};
var result = datasource.BuildQuery(query);
//The resulting query would be best described with the follwing expression:
dataSource.Where(x => x.Name.Contains("Fo") && x.Price == 22 && x.BrandName.StartsWith("Fa"));
Same rules apply as with QueryProperty except that it is designed to work to compare against several properties at the same time. Use it with at least 3 parameters - operator and two or more properties.
Example:
public class TestUserSearch
{
[QueryProperties(ComparisonOperator.Contains, "FirstName", "Username")]
public string Keyword { get; set; }
}
var result = datasource.BuildQuery(new TestUserSearch { Keyword = "Fa" });
//The resulting query would be best described with the follwing expression:
dataSource.Where(x => x.FirstName.Contains("Fa") || x.Username.Contains("Fa"));
This attribute is used for collection navigation properties, when the predicate should not be applied directly to the object of interest, but to a collection it holds. When used, if only one collection element matches the predicate, parent object is contained within the result.
Example: we have a database table for product, where each product can have many attributes and we want to filter out the products based on the attribute name - to return only products whose attribute list contains an attribute of provided name.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// other properties
public ICollection<Attribute> Attributes { get; set; }
}
public class Attribute
{
public int Id { get; set; }
public string Name { get; set; }
}
Query object:
public class ProductQuery
{
[WithAnyProperty("Attributes.Name")]
public string AttributeName { get; set; }
}
var result = productsQueryable.BuildQuery(new ProductQuery { AttributeName = "Fa" });
The resulting query will contain only the products conaining at least one attribute named "Fa".
Used to determine whether or not an entity has a value inside a navigation reference. Decorates only boolean properties.
Example - we want returned all the cars having an owner.
public class Car
{
public int Id { get; set; }
public string Model { get; set; }
public Person Owner { get; set; }
}
public class Person
{
public int Id [ get; set; }
public string FirstName { get; set; }
public ICollection<Car> Cars { get; set; }
}
//more code
public class CarQuery
{
[WithProperty]
public bool? WithOwner { get; set; }
}
var result = Context.Cars.BuildQuery(new CarQuery { WithOwner = true });
The resulting query will returned all the car's whose Owner property is not null.