Nymezide.Specification
1.0.3
dotnet add package Nymezide.Specification --version 1.0.3
NuGet\Install-Package Nymezide.Specification -Version 1.0.3
<PackageReference Include="Nymezide.Specification" Version="1.0.3" />
paket add Nymezide.Specification --version 1.0.3
#r "nuget: Nymezide.Specification, 1.0.3"
// Install Nymezide.Specification as a Cake Addin
#addin nuget:?package=Nymezide.Specification&version=1.0.3
// Install Nymezide.Specification as a Cake Tool
#tool nuget:?package=Nymezide.Specification&version=1.0.3
Паттерн Спецификация на .NET
Определение
«Спецификация» в программировании — это шаблон проектирования, посредством которого представление правил бизнес логики может быть преобразовано в виде цепочки объектов, связанных операциями булевой логики.
Область применения
- Валидация объекта в памяти на соответствие требованиям
- Поиск объектов в базе данных, соответствующих требованиям
Создание экземпляра объекта по требованиям- это невозможно, т.к. спецификация инкапсулирует требования внутри себя. случайный перебор всех вариантов - это плохой подход.
Возможности этой библиотеки
- Cтрого-типизированные спецификации
- Динамические спецификации
- однострочные спецификации прямо в коде
- Запросы в Базу Данных, а также коллекции в памяти, через Linq
- Fluent-интерфейс
.And()
.Or()
.Not()
- Поддержка операторов
&
|
!
==
!=
- Множество методов расширений для коллекций
.Is()
.IsAny()
.IsAll()
.AnyIs()
.AllIs()
- Реактивные экшен-методы для выполнения действия, при False результате
- например можно подсчитать сколько объектов из коллекции не соответствуют спецификации
- или выполнить какой-либо метод для объектов из коллекции, которые не соответствуют специцикации
Cтрого-типизированные спецификации
Базовый абстрактный класс, который необходимо наследовать и реализовать
public abstract class AbstractSpec<T> : ISpecification<T>
{
public abstract Expression<Func<T, bool>> Expression { get; }
public bool IsSatisfiedBy(T candidate)
{
// implementation
}
}
Пример:
// Скучный фильм
public class BoringMovieSpec : AbstractSpec<Movie>
{
private readonly TimeSpan _durationGreaterThat;
private readonly double _ratingLessOrEqualThat;
public BoringMovieSpec(TimeSpan? durationGreaterThat = null, double ratingLessOrEqualThat = 4)
{
_durationGreaterThat = durationGreaterThat ?? TimeSpan.FromHours(2.5);
_ratingLessOrEqualThat = ratingLessOrEqualThat;
}
public override Expression<Func<Movie, bool>> Expression
=> movie => movie.Duration > _durationGreaterThat &&
movie.Rating <= _ratingLessOrEqualThat;
}
Динамические спецификации
AbstractSpec<Movie> spec = new Spec<Movie>(m => m.Rating > 5 && m.MpaaRating == MpaaRating.PG13);
Запросы в Базу Данных, а также коллекции в памяти, через Linq
using var db = new OrdersDbContext();
var richProductsSpec = new Spec<Order>(x => x.Products.Any(p => p.Price >= 500));
var query = db.Orders
.Include(o => o.Products)
.Where(richProductsSpec);
var orders = query.ToList();
Fluent-интерфейс
AbstractSpec<Movie> longest = new Spec<Movie>(m => m.Duration > TimeSpan.FromHours(2.5));
AbstractSpec<Movie> ratingLess = new Spec<Movie>(m => m.Rating <= 3);
var combineSpecs1 = longest.And(ratingLess);
var combineSpecs2 = longest.Or(ratingLess);
Поддержка операторов
AbstractSpec<Movie> longest = new Spec<Movie>(m => m.Duration > TimeSpan.FromHours(2.5));
AbstractSpec<Movie> ratingLess = new Spec<Movie>(m => m.Rating <= 3);
var combineSpecs1 = longest & ratingLess;
var combineSpecs2 = longest | ratingLess;
Множество методов расширений для коллекций
Movie movie = new Movie()
{
Rating = 3,
};
var spec1 = new Spec<Movie>(m => m.Rating == 2);
var spec2 = new Spec<Movie>(m => m.Rating == 3);
var spec3 = new Spec<Movie>(m => m.Rating == 4);
var result = movie.IsAny(spec1, spec2, spec3);
ICollection<Movie> movies = new List<Movie>
{
new Movie { Rating = 2 },
new Movie { Rating = 3 },
new Movie { Rating = 4 },
new Movie { Rating = 5 },
new Movie { Rating = 6 },
};
var spec = new Spec<Movie>(m => m.Rating > 4);
var result = movies.AnyIs(spec);
Реактивные экшен-методы для выполнения действия, при False результате
OnFalseAction
выполняется только при выполнении метода IsSatisfiedBy()
, а значит только при вызове операторов:
.Is()
, .IsAny()
, .IsAll()
, .AnyIs()
, .AllIs()
простой пример на проверку одного объекта:
Movie movie = new Movie()
{
Rating = 4,
MpaaRating = MpaaRating.G
};
var spec = new Spec<Movie>(m => m.Rating > 5 && m.MpaaRating == MpaaRating.R);
var actionValue = false;
spec.OnFalseAction = (s, c) => actionValue = true;
var result = movie.Is(spec); // actionValue станет true, т.к. спецификация вернет False
сложный пример, проверка коллекции через методы расширения:
IEnumerable<Movie> movies = new List<Movie>
{
new Movie { Rating = 2 },
new Movie { Rating = 3 },
new Movie { Rating = 4 },
new Movie { Rating = 5 },
new Movie { Rating = 6 },
};
var counter = 0;
var spec = new Spec<Movie>(m => m.Rating > 4)
{
OnFalseAction = (spec, candidate) => counter++
};
var result1 = movies.AllIs(spec); // сработает для всех кто вернул false, в данном случае counter будет равен 3, т.к. всего 3 объекта не соответсвуют
var result2 = movies.AnyIs(spec); // сработает для всех, если никто не вернул true, в данном случае counter будет равен 0 - т.к. в коллекции есть хотябы один объект попадающий под спецификацию
OnFalseAction
не выполняется при запросах в БД или запросах в коллекции через Linq
Подключение библиотеки через Nuget
Install-Package Nymezide.Specification
Поддержка фрейморков
- .NET Standard 2.1
- .NET 5.0
- .NET 6.0
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 is compatible. net5.0-windows was computed. net6.0 is compatible. 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. |
.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
- No dependencies.
-
net5.0
- No dependencies.
-
net6.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.