https://github.com/baune8d/linqbuilder
LinqBuilder is an advanced implementation of the specification pattern specifically targeting LINQ query generation.
https://github.com/baune8d/linqbuilder
csharp dotnet dotnet-core dotnet-standard dotnet5 dotnet6 entity-framework entity-framework-core linq specification-pattern
Last synced: 10 months ago
JSON representation
LinqBuilder is an advanced implementation of the specification pattern specifically targeting LINQ query generation.
- Host: GitHub
- URL: https://github.com/baune8d/linqbuilder
- Owner: Baune8D
- License: mit
- Created: 2017-07-16T22:08:12.000Z (over 8 years ago)
- Default Branch: main
- Last Pushed: 2024-12-30T20:35:03.000Z (about 1 year ago)
- Last Synced: 2025-03-29T22:07:43.786Z (10 months ago)
- Topics: csharp, dotnet, dotnet-core, dotnet-standard, dotnet5, dotnet6, entity-framework, entity-framework-core, linq, specification-pattern
- Language: C#
- Homepage:
- Size: 440 KB
- Stars: 34
- Watchers: 4
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# LinqBuilder
[](https://ci.appveyor.com/project/Baune8D/linqbuilder)
[](https://codecov.io/gh/Baune8D/linqbuilder)
[](https://www.nuget.org/packages/LinqBuilder)
**Available on NuGet:** [https://www.nuget.org/packages/LinqBuilder/](https://www.nuget.org/packages/LinqBuilder/)
**MyGet development feed:** [https://www.myget.org/F/baunegaard/api/v3/index.json](https://www.myget.org/F/baunegaard/api/v3/index.json)
LinqBuilder is based on the specification pattern.
## Table of Contents
1. [LinqBuilder Specifications](#linqbuilder-specifications)
2. [LinqBuilder OrderSpecifications](#linqbuilder-orderspecifications)
3. [LinqBuilder.EFCore / LinqBuilder.EF6](#linqbuilderefcore--linqbuilderef6)
4. [Full Example](#full-example)
## LinqBuilder Specifications
### Usage
Specifications can be constructed in three different ways.
**By extending Specification:**
```csharp
public class FirstnameIsFoo : Specification
{
public override Expression> AsExpression()
{
return person => person.Firstname == "Foo";
}
}
ISpecification firstnameIsFoo = new FirstnameIsFoo();
```
**By extending DynamicSpecification:**
```csharp
public class FirstnameIs : DynamicSpecification
{
public override Expression> AsExpression()
{
return person => person.Firstname == Value;
}
}
ISpecification firstnameIsFoo = new FirstnameIs().Set("Foo");
```
**By extending MultiSpecification:**
```csharp
public class FirstnameIsFoo : MultiSpecification
{
public override Expression> AsExpressionForEntity1()
{
return person => person.Firstname == "Foo";
}
public override Expression> AsExpressionForEntity2()
{
return person => person.Firstname == "Foo";
}
}
ISpecification firstnameIsFoo = new FirstnameIsFoo(); // First generic is default
ISpecification firstnameIsFoo = new FirstnameIsFoo().For();
ISpecification firstnameIsFoo = new FirstnameIsFoo().For();
```
**By static New method:**
```csharp
ISpecification firstnameIsFoo = Specification.New(p => p.Firstname == "Foo");
// Or by alias
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
```
### Example
```csharp
var collection = new List() { ... };
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
ISpecification firstnameIsBar = Spec.New(p => p.Firstname == "Bar");
ISpecification specification = firstnameIsFoo.Or(firstnameIsBar);
var result = collection.ExeSpec(specification).ToList();
// result = Collection items satisfied by specification
```
The extension ```ExeSpec``` allows all types of ```ISpecification``` to be executed on ```IQueryable``` and ```IEnumerable```.
### Methods
```csharp
ISpecification specification = Spec.All(
new SomeSpecification(),
new SomeOtherSpecification(),
...
);
ISpecification specification = Spec.None(
new SomeSpecification(),
new SomeOtherSpecification(),
...
);
ISpecification specification = Spec.Any(
new SomeSpecification(),
new SomeOtherSpecification(),
...
);
```
### Extensions
```csharp
ISpecification And(this ISpecification left, ISpecification right);
ISpecification Or(this ISpecification left, ISpecification right);
ISpecification Not(this ISpecification specification);
bool IsSatisfiedBy(this ISpecification specification, TEntity entity);
ISpecification Clone(this ISpecification specification);
```
**LinqBuilder** also extends the following extensions to support ```ISpecification``` on ```IQueryable``` and ```IEnumerable```.
```csharp
IEnumerable collection = collection.Where(specification);
bool result = collection.Any(specification);
bool result = collection.All(specification);
int result = collection.Count(specification);
Entity result = collection.First(specification);
Entity result = collection.FirstOrDefault(specification);
Entity result = collection.Single(specification);
Entity result = collection.SingleOrDefault(specification);
```
## LinqBuilder OrderSpecifications
### Usage
Order specifications can be constructed in almost the same way as regular specifications.
**By extending OrderSpecification:**
```csharp
public class FirstnameDescending : OrderSpecification
{
public DescNumberOrderSpecification() : base(Sort.Descending) { }
public override Expression> AsExpression()
{
return person => person.Firstname;
}
}
ISpecification firstnameDescending = new FirstnameDescending();
```
**By static New method:**
```csharp
ISpecification firstnameDescending = OrderSpecification.New(p => p.Firstname, Sort.Descending);
// Or by alias
ISpecification firstnameDescending = OrderSpec.New(p => p.Firstname, Sort.Descending);
```
### Example
```csharp
var collection = new List() { ... };
ISpecification firstnameDescending = OrderSpec.New(p => p.Firstname, Sort.Descending);
ISpecification lastnameDescending = OrderSpec.New(p => p.Lastname, Sort.Descending);
ISpecification specification = firstnameDescending.ThenBy(lastnameDescending);
var result = collection.ExeSpec(specification).ToList();
// result = Collection ordered by descending number, then by other number
```
### Methods
```csharp
ISpecification specification = OrderSpec.New(p => p.Firstname)
.Take(10);
ISpecification specification = OrderSpec.New(p => p.Firstname)
.Skip(5);
ISpecification specification = OrderSpec.New(p => p.Firstname)
.Paginate(2, 10); // Equals .Skip((2 - 1) * 10).Take(10)
```
### Extensions
```csharp
IOrderedEnumerable collection = collection
.OrderBy(specification);
.ThenBy(otherSpecification);
```
Order specifications can also be chained with regular LinqBuilder specifications.
```csharp
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
ISpecification firstnameAscending = OrderSpec.New(p => p.Firstname);
ISpecification specification = firstnameIsFoo.OrderBy(firstnameAscending);
```
Chained ```OrderSpecification```'s can also be attatched to a specification later.
```csharp
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
ISpecification firstnameAscending = OrderSpec.New(p => p.Firstname);
ISpecification lastnameAscending = OrderSpec.New(p => p.Firstname);
ISpecification orderSpecification = firstnameAscending.ThenBy(lastnameAscending);
ISpecification specification = firstnameIsFoo.UseOrdering(orderSpecification);
```
The following extensions will help to check what kind of ordering is applied.
```csharp
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
ISpecification firstnameAscending = OrderSpec.New(p => p.Firstname);
firstnameIsFoo.IsOrdered(); // Returns false
ISpecification specification = firstnameIsFoo.OrderBy(firstnameAscending);
specification.IsOrdered(); // Returns true
```
```csharp
ISpecification specification = Spec.New(p => p.Firstname == "Foo");
specification.HasSkip(); // Returns false
ISpecification specification = specification.Skip(10);
specification.HasSkip(); // Returns true
```
```csharp
ISpecification specification = Spec.New(p => p.Firstname == "Foo");
specification.HasTake(); // Returns false
ISpecification specification = specification.Take(10);
specification.HasTake(); // Returns true
```
## LinqBuilder.EFCore / LinqBuilder.EF6
| Package | Version |
| -------------------|:---------------------------------------------------------------------------------------------------------------------:|
| LinqBuilder.EFCore | [](https://www.nuget.org/packages/LinqBuilder.EFCore) |
| LinqBuilder.EF6 | [](https://www.nuget.org/packages/LinqBuilder.EF6) |
### Extensions
**LinqBuilder.EF** packages extends the following extensions to support ```ISpecification```.
```csharp
bool result = await _sampleContext.Entities.AnyAsync(specification);
bool result = await _sampleContext.Entities.AllAsync(specification);
int result = await _sampleContext.Entities.CountAsync(specification);
Entity result = await _sampleContext.Entities.FirstAsync(specification);
Entity result = await _sampleContext.Entities.FirstOrDefaultAsync(specification);
Entity result = await _sampleContext.Entities.SingleAsync(specification);
Entity result = await _sampleContext.Entities.SingleOrDefaultAsync(specification);
```
## Full example
```csharp
public class Person
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
public class SampleDbContext : DbContext // Simplified DbContext
{
public virtual DbSet Persons { get; set; }
}
public class DbService where TEntity : class
{
private readonly DbSet _dbSet;
public DbService(SampleDbContext context)
{
_dbSet = context.Set();
}
public int Count(ISpecification specification)
{
return _dbSet.Count(specification);
}
public List Get(ISpecification specification)
{
return _dbSet.ExeSpec(specification).ToList();
}
public (List items, int count) GetAndCount(ISpecification specification)
{
return (Get(specification), Count(specification));
}
}
ISpecification firstnameIsFoo = Spec.New(p => p.Firstname == "Foo");
ISpecification lastnameIsBar = Spec.New(p => p.Lastname == "Bar");
ISpecification idDescending = OrderSpec.New(p => p.Id, Sort.Descending);
ISpecification specification = firstnameIsFoo.And(lastnameIsBar)
.OrderBy(idDescending)
.Paginate(1, 5); // pageNo = 1, pageSize = 5
using (var context = new SampleDbContext())
{
var result = new DbService(context).GetAndCount(specification);
// result.items = Paginated list of Person's with name: Foo Bar
// result.count = Total unpaginated result count
}
```