Skip to content

Commit

Permalink
Merge pull request #22 from fiseni/version11
Browse files Browse the repository at this point in the history
Version 11 beta1
  • Loading branch information
fiseni authored Nov 5, 2024
2 parents 6c57335 + f6f1c5d commit 748ab7d
Show file tree
Hide file tree
Showing 92 changed files with 3,522 additions and 1,609 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -386,3 +386,4 @@ FodyWeavers.xsd

# Developer config files
**/appsettings.Development*.json
*V11.csproj
93 changes: 85 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ A .NET library for building query specifications.
- [Pozitron.QuerySpecification.EntityFrameworkCore](https://www.nuget.org/packages/Pozitron.QuerySpecification.EntityFrameworkCore)
An `EntityFramework Core` plugin to the base package. It contains EF specific evaluators.

## Usage
## Getting Started

Create your specification classes by inheriting from the `Specification<T>` class, and use the builder `Query` to build your queries in the constructor.
An extended list of features (in-memory collection evaluations, validations, repositories, extensions, etc.) will be soon available in the Wiki. Below are listed some basic and common usages.

### Creating and consuming specifications

Create your specifications by inheriting from the `Specification<T>` class, and use the `Query` builder in the constructor to define your conditions.

```csharp
public class CustomerSpec : Specification<Customer>
{
public CustomerSpec(int age, string nameTerm)
{
Query
.Where(x => x.Age > age)
.Where(x => x.Age >= age)
.Like(x => x.Name, $"%{nameTerm}%")
.Include(x => x.Addresses)
.ThenInclude(x => x.Contact)
Expand All @@ -37,10 +41,10 @@ public class CustomerSpec : Specification<Customer>
}
```

Apply the specification to `DbSet<T>` or any `IQueryable<T>` source.
Apply the specification to `DbSet<T>` or `IQueryable<T>` source.

```csharp
var spec = new CustomerSpec(30, "John");
var spec = new CustomerSpec(30, "Customer");

List<Customer> result = await _context
.Customers
Expand All @@ -58,24 +62,97 @@ public class CustomerDtoSpec : Specification<Customer, CustomerDto>
public CustomerDtoSpec(int age, string nameTerm)
{
Query
.Where(x => x.Age > age)
.Where(x => x.Age >= age)
.Like(x => x.Name, $"%{nameTerm}%")
.OrderBy(x => x.Name)
.Select(x => new CustomerDto(x.Id, x.Name));
}
}
```

Apply the specification to `DbSet<T>` or any `IQueryable<T>` source.
Apply the specification to `DbSet<T>` or `IQueryable<T>` source.

```csharp
var spec = new CustomerSpec(30, "John");
var spec = new CustomerDtoSpec(30, "Customer");

List<CustomerDto> result = await _context
.Customers
.WithSpecification(spec)
.ToListAsync();
```

### Pagination

The library defines a convenient `ToPagedResult` extension method that returns a detailed paginated result.

```csharp
var spec = new CustomerDtoSpec(1, "Customer");
var pagingFilter = new PagingFilter
{
Page = 1,
PageSize = 2
};

PagedResult<CustomerDto> result = await _context
.Customers
.WithSpecification(spec)
.ToPagedResultAsync(pagingFilter);
```

The `PagedResult<T>` is serializable and contains a detailed pagination information and the data.

```json
{
"Pagination": {
"TotalItems": 100,
"TotalPages": 50,
"PageSize": 2,
"Page": 1,
"StartItem": 1,
"EndItem": 2,
"HasPrevious": false,
"HasNext": true
},
"Data": [
{
"Id": 1,
"Name": "Customer 1"
},
{
"Id": 2,
"Name": "Customer 2"
}
]
}
```

## Benchmarks

In version 11, we refactored and rebuilt the internals from the ground up. The new version reduces the memory footprint drastically. The overhead of the library is now negligible and statistically insignificant. Here are the benchmark results of `ToQueryString()` for various queries. Refer to the [Benchmarks](https://github.com/fiseni/QuerySpecification/tree/main/tests/QuerySpecification.Benchmarks/Benchmarks) project for more benchmarks.

Type:
- 0 -> Empty
- 1 -> Single Where clause
- 2 -> Where and OrderBy
- 3 -> Where, Order chain, Include chain, Flag (AsNoTracking)
- 4 -> Where, Order chain, Include chain, Like, Skip, Take, Flag (AsNoTracking)

| Method | Type | Mean | Error | StdDev | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
|------- |----- |----------:|---------:|---------:|------:|--------:|-------:|----------:|------------:|
| EFCore | 0 | 81.55 us | 0.686 us | 0.608 us | 1.00 | 10.0098 | 0.9766 | 82.54 KB | 1.00 |
| Spec | 0 | 78.18 us | 0.472 us | 0.441 us | 0.96 | 10.0098 | 0.9766 | 82.53 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 1 | 92.62 us | 0.350 us | 0.310 us | 1.00 | 10.2539 | 0.9766 | 84.77 KB | 1.00 |
| Spec | 1 | 92.92 us | 0.252 us | 0.236 us | 1.00 | 10.2539 | 0.9766 | 84.84 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 2 | 95.48 us | 0.654 us | 0.580 us | 1.00 | 10.2539 | 0.9766 | 86.03 KB | 1.00 |
| Spec | 2 | 98.36 us | 0.775 us | 0.687 us | 1.03 | 10.2539 | 0.4883 | 86.12 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 3 | 106.62 us | 0.684 us | 0.606 us | 1.00 | 10.7422 | 0.4883 | 90.35 KB | 1.00 |
| Spec | 3 | 109.56 us | 0.700 us | 0.655 us | 1.03 | 10.7422 | 0.4883 | 90.64 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 4 | 147.47 us | 0.619 us | 0.483 us | 1.00 | 13.1836 | 0.9766 | 110.78 KB | 1.00 |
| Spec | 4 | 150.82 us | 0.538 us | 0.449 us | 1.02 | 13.1836 | 0.9766 | 111.32 KB | 1.00 |

## Give a Star! :star:
If you like or are using this project please give it a star. Thanks!
1 change: 1 addition & 0 deletions exclusion.dic
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ aaab
aaaab
aaaaab
axza
Compilable
93 changes: 85 additions & 8 deletions readme-nuget.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@ A .NET library for building query specifications.
- [Pozitron.QuerySpecification.EntityFrameworkCore](https://www.nuget.org/packages/Pozitron.QuerySpecification.EntityFrameworkCore)
An `EntityFramework Core` plugin to the base package. It contains EF specific evaluators.

## Usage
## Getting Started

Create your specification classes by inheriting from the `Specification<T>` class, and use the builder `Query` to build your queries in the constructor.
An extended list of features (in-memory collection evaluations, validations, repositories, extensions, etc.) will be soon available in the Wiki. Below are listed some basic and common usages.

### Creating and consuming specifications

Create your specifications by inheriting from the `Specification<T>` class, and use the `Query` builder in the constructor to define your conditions.

```csharp
public class CustomerSpec : Specification<Customer>
{
public CustomerSpec(int age, string nameTerm)
{
Query
.Where(x => x.Age > age)
.Where(x => x.Age >= age)
.Like(x => x.Name, $"%{nameTerm}%")
.Include(x => x.Addresses)
.ThenInclude(x => x.Contact)
Expand All @@ -27,10 +31,10 @@ public class CustomerSpec : Specification<Customer>
}
```

Apply the specification to `DbSet<T>` or to any `IQueryable<T>` source.
Apply the specification to `DbSet<T>` or `IQueryable<T>` source.

```csharp
var spec = new CustomerSpec(30, "John");
var spec = new CustomerSpec(30, "Customer");

List<Customer> result = await _context
.Customers
Expand All @@ -48,24 +52,97 @@ public class CustomerDtoSpec : Specification<Customer, CustomerDto>
public CustomerDtoSpec(int age, string nameTerm)
{
Query
.Where(x => x.Age > age)
.Where(x => x.Age >= age)
.Like(x => x.Name, $"%{nameTerm}%")
.OrderBy(x => x.Name)
.Select(x => new CustomerDto(x.Id, x.Name));
}
}
```

Apply the specification to `DbSet<T>` or any `IQueryable<T>` source.
Apply the specification to `DbSet<T>` or `IQueryable<T>` source.

```csharp
var spec = new CustomerSpec(30, "John");
var spec = new CustomerDtoSpec(30, "Customer");

List<CustomerDto> result = await _context
.Customers
.WithSpecification(spec)
.ToListAsync();
```

### Pagination

The library defines a convenient `ToPagedResult` extension method that returns a detailed paginated result.

```csharp
var spec = new CustomerDtoSpec(1, "Customer");
var pagingFilter = new PagingFilter
{
Page = 1,
PageSize = 2
};

PagedResult<CustomerDto> result = await _context
.Customers
.WithSpecification(spec)
.ToPagedResultAsync(pagingFilter);
```

The `PagedResult<T>` is serializable and contains a detailed pagination information and the data.

```json
{
"Pagination": {
"TotalItems": 100,
"TotalPages": 50,
"PageSize": 2,
"Page": 1,
"StartItem": 1,
"EndItem": 2,
"HasPrevious": false,
"HasNext": true
},
"Data": [
{
"Id": 1,
"Name": "Customer 1"
},
{
"Id": 2,
"Name": "Customer 2"
}
]
}
```

## Benchmarks

In version 11, we refactored and rebuilt the internals from the ground up. The new version reduces the memory footprint drastically. The overhead of the library is now negligible and statistically insignificant. Here are the benchmark results of `ToQueryString()` for various queries. Refer to the [Benchmarks](https://github.com/fiseni/QuerySpecification/tree/main/tests/QuerySpecification.Benchmarks/Benchmarks) project for more benchmarks.

Type:
- 0 -> Empty
- 1 -> Single Where clause
- 2 -> Where and OrderBy
- 3 -> Where, Order chain, Include chain, Flag (AsNoTracking)
- 4 -> Where, Order chain, Include chain, Like, Skip, Take, Flag (AsNoTracking)

| Method | Type | Mean | Error | StdDev | Ratio | Gen0 | Gen1 | Allocated | Alloc Ratio |
|------- |----- |----------:|---------:|---------:|------:|--------:|-------:|----------:|------------:|
| EFCore | 0 | 81.55 us | 0.686 us | 0.608 us | 1.00 | 10.0098 | 0.9766 | 82.54 KB | 1.00 |
| Spec | 0 | 78.18 us | 0.472 us | 0.441 us | 0.96 | 10.0098 | 0.9766 | 82.53 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 1 | 92.62 us | 0.350 us | 0.310 us | 1.00 | 10.2539 | 0.9766 | 84.77 KB | 1.00 |
| Spec | 1 | 92.92 us | 0.252 us | 0.236 us | 1.00 | 10.2539 | 0.9766 | 84.84 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 2 | 95.48 us | 0.654 us | 0.580 us | 1.00 | 10.2539 | 0.9766 | 86.03 KB | 1.00 |
| Spec | 2 | 98.36 us | 0.775 us | 0.687 us | 1.03 | 10.2539 | 0.4883 | 86.12 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 3 | 106.62 us | 0.684 us | 0.606 us | 1.00 | 10.7422 | 0.4883 | 90.35 KB | 1.00 |
| Spec | 3 | 109.56 us | 0.700 us | 0.655 us | 1.03 | 10.7422 | 0.4883 | 90.64 KB | 1.00 |
| | | | | | | | | | |
| EFCore | 4 | 147.47 us | 0.619 us | 0.483 us | 1.00 | 13.1836 | 0.9766 | 110.78 KB | 1.00 |
| Spec | 4 | 150.82 us | 0.538 us | 0.449 us | 1.02 | 13.1836 | 0.9766 | 111.32 KB | 1.00 |

## Give a Star! :star:
If you like or are using this project please give it a star. Thanks!
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Pozitron.QuerySpecification;

public class AsNoTrackingEvaluator : IEvaluator
public sealed class AsNoTrackingEvaluator : IEvaluator
{
private AsNoTrackingEvaluator() { }
public static AsNoTrackingEvaluator Instance = new();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Pozitron.QuerySpecification;

public class AsNoTrackingWithIdentityResolutionEvaluator : IEvaluator
public sealed class AsNoTrackingWithIdentityResolutionEvaluator : IEvaluator
{
private AsNoTrackingWithIdentityResolutionEvaluator() { }
public static AsNoTrackingWithIdentityResolutionEvaluator Instance = new();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Pozitron.QuerySpecification;

public class AsSplitQueryEvaluator : IEvaluator
public sealed class AsSplitQueryEvaluator : IEvaluator
{
private AsSplitQueryEvaluator() { }
public static AsSplitQueryEvaluator Instance = new();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Pozitron.QuerySpecification;

public class IgnoreQueryFiltersEvaluator : IEvaluator
public sealed class IgnoreQueryFiltersEvaluator : IEvaluator
{
private IgnoreQueryFiltersEvaluator() { }
public static IgnoreQueryFiltersEvaluator Instance = new();
Expand Down
Loading

0 comments on commit 748ab7d

Please sign in to comment.