Repository: navozenko/LinqSpecs
Branch: master
Commit: 0374db964085
Files: 33
Total size: 55.4 KB
Directory structure:
gitextract_i9vq50lx/
├── .editorconfig
├── .gitattributes
├── .gitignore
├── LinqSpecs/
│ ├── AdHocSpecification.cs
│ ├── FalseSpecification.cs
│ ├── LinqSpecs.csproj
│ ├── Operators/
│ │ ├── AndSpecification.cs
│ │ ├── NotSpecification.cs
│ │ └── OrSpecification.cs
│ ├── Specification.cs
│ ├── TrueSpecification.cs
│ └── Utilities/
│ ├── ExpressionExtensions.cs
│ ├── ExpressionParameterRebinder.cs
│ └── HashCodeHelpers.cs
├── LinqSpecs.DatabaseTests/
│ ├── DomainModel/
│ │ ├── Customer.cs
│ │ ├── Order.cs
│ │ └── SampleDbContext.cs
│ ├── LinqSpecs.DatabaseTests.csproj
│ └── Tests.cs
├── LinqSpecs.UnitTests/
│ ├── AdHocSpecificationTests.cs
│ ├── AssemblyTests.cs
│ ├── FalseSpecificationTests.cs
│ ├── Helpers/
│ │ └── SampleRepository.cs
│ ├── LinqSpecs.UnitTests.csproj
│ ├── Operators/
│ │ ├── AndSpecificationTests.cs
│ │ ├── NotSpecificationTests.cs
│ │ └── OrSpecificationTests.cs
│ ├── SpecificationTests.cs
│ └── TrueSpecificationTests.cs
├── LinqSpecs.sln
├── LinqSpecs.snk
├── README.md
└── license.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig to support per-solution formatting
# See: https://aka.ms/editorconfigdocs
root = true
[*]
indent_style = space
trim_trailing_whitespace = true
charset = utf-8-bom
insert_final_newline = true
[*.{cs}]
indent_size = 4
dotnet_sort_system_directives_first = true
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
indent_size = 2
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
## TODO: Comment the next line if you want to checkin your
## web deploy settings but do note that will include unencrypted
## passwords
#*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
================================================
FILE: LinqSpecs/AdHocSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs
{
/// <summary>
/// Allows to write a <see cref="Specification{T}"/> without writing a class.
/// </summary>
public class AdHocSpecification<T> : Specification<T>
{
private readonly Lazy<string> _predicateString;
public Expression<Func<T, bool>> Predicate { get; }
/// <summary>
/// Creates a custom ad-hoc <see cref="Specification{T}"/> from the given predicate expression.
/// </summary>
public AdHocSpecification(Expression<Func<T, bool>> predicate)
{
Predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
_predicateString = new Lazy<string>(() => Predicate.ToString());
}
/// <summary>
/// Returns an expression that defines this query.
/// </summary>
public override Expression<Func<T, bool>> ToExpression()
{
return Predicate;
}
/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is AdHocSpecification<T> otherSpec)
return _predicateString.Value.Equals(otherSpec._predicateString.Value);
return false;
}
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
public override int GetHashCode()
{
return HashCodeHelpers.Combine(_predicateString.Value, GetType());
}
}
}
================================================
FILE: LinqSpecs/FalseSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs
{
/// <summary>
/// Specification which is not satisfied by any object.
/// </summary>
public class FalseSpecification<T> : Specification<T>
{
/// <summary>
/// Returns an expression that defines this query.
/// </summary>
public override Expression<Func<T, bool>> ToExpression()
{
return x => false;
}
/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return GetType() == other.GetType();
}
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
public override int GetHashCode()
{
return HashCodeHelpers.Combine(GetType());
}
}
}
================================================
FILE: LinqSpecs/LinqSpecs.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<Version>3.2.0</Version>
<Authors>José F. Romaniello, Sergey Navozenko</Authors>
<Company></Company>
<Copyright>Copyright © 2010-2022</Copyright>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseExpression>MS-PL</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/navozenko/LinqSpecs</PackageProjectUrl>
<PackageTags>linq specification spec ddd</PackageTags>
<RepositoryUrl>https://github.com/navozenko/LinqSpecs</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>1591</NoWarn>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../LinqSpecs.snk</AssemblyOriginatorKeyFile>
<Description>LinqSpecs is a framework that will help you to create specifications for LINQ queries.
Supported platforms:
- .NET Standard 1.0+
- .NET Framework 4.5+
- .NET Core 2.0+
- .NET 5.0+</Description>
</PropertyGroup>
<ItemGroup>
<Content Include="..\license.txt" PackagePath="license.txt" />
</ItemGroup>
</Project>
================================================
FILE: LinqSpecs/Operators/AndSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs.Operators
{
/// <summary>
/// Combines two specifications by using logical AND operation.
/// </summary>
public class AndSpecification<T> : Specification<T>
{
public Specification<T> Left { get; }
public Specification<T> Right { get; }
public AndSpecification(Specification<T> left, Specification<T> right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
}
public override Expression<Func<T, bool>> ToExpression()
{
var expr1 = Left.ToExpression();
var expr2 = Right.ToExpression();
return expr1.AndAlso(expr2);
}
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is AndSpecification<T> otherSpec)
return Left.Equals(otherSpec.Left) && Right.Equals(otherSpec.Right);
return false;
}
public override int GetHashCode()
{
return HashCodeHelpers.Combine(Left, Right, GetType());
}
}
}
================================================
FILE: LinqSpecs/Operators/NotSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs.Operators
{
/// <summary>
/// Negates a source specification.
/// </summary>
public class NotSpecification<T> : Specification<T>
{
public Specification<T> Source { get; }
public NotSpecification(Specification<T> source)
{
Source = source ?? throw new ArgumentNullException(nameof(source));
}
public override Expression<Func<T, bool>> ToExpression()
{
var expr = Source.ToExpression();
return Expression.Lambda<Func<T, bool>>(Expression.Not(expr.Body), expr.Parameters);
}
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is NotSpecification<T> otherSpec)
return Source.Equals(otherSpec.Source);
return false;
}
public override int GetHashCode()
{
return HashCodeHelpers.Combine(Source, GetType());
}
}
}
================================================
FILE: LinqSpecs/Operators/OrSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs.Operators
{
/// <summary>
/// Combines two specifications by using logical OR operation.
/// </summary>
public class OrSpecification<T> : Specification<T>
{
public Specification<T> Left { get; }
public Specification<T> Right { get; }
public OrSpecification(Specification<T> left, Specification<T> right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
}
public override Expression<Func<T, bool>> ToExpression()
{
var expr1 = Left.ToExpression();
var expr2 = Right.ToExpression();
return expr1.OrElse(expr2);
}
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is OrSpecification<T> otherSpec)
return Left.Equals(otherSpec.Left) && Right.Equals(otherSpec.Right);
return false;
}
public override int GetHashCode()
{
return HashCodeHelpers.Combine(Left, Right, GetType());
}
}
}
================================================
FILE: LinqSpecs/Specification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Operators;
namespace LinqSpecs
{
/// <summary>
/// Base class for query specifications that can be combined
/// using logical AND, OR and NOT operators.
/// </summary>
public abstract class Specification<T>
{
/// <summary>
/// Returns an expression that defines this query.
/// </summary>
/// <remarks>
/// Typically calling this method is not needed as the query
/// specification can be converted implicitly to an expression
/// by just assigning it or passing it as such to another method.
/// </remarks>
public abstract Expression<Func<T, bool>> ToExpression();
/// <summary>
/// Performs an implicit conversion from <see cref="Specification{T}"/>
/// to a LINQ expression.
/// </summary>
public static implicit operator Expression<Func<T, bool>>(Specification<T> spec)
{
return spec?.ToExpression() ?? throw new ArgumentNullException(nameof(spec));
}
/// <summary>
/// Override operator false for supporting && and || operations
/// </summary>
public static bool operator false(Specification<T> spec)
{
return false;
}
/// <summary>
/// Override operator true for supporting && and || operations
/// </summary>
public static bool operator true(Specification<T> spec)
{
return false;
}
/// <summary>
/// Allows to combine two query specifications using a logical AND operation.
/// </summary>
public static Specification<T> operator &(Specification<T> spec1, Specification<T> spec2)
{
return new AndSpecification<T>(spec1, spec2);
}
/// <summary>
/// Allows to combine two query specifications using a logical OR operation.
/// </summary>
public static Specification<T> operator |(Specification<T> spec1, Specification<T> spec2)
{
return new OrSpecification<T>(spec1, spec2);
}
/// <summary>
/// Negates the given specification.
/// </summary>
public static Specification<T> operator !(Specification<T> spec)
{
return new NotSpecification<T>(spec);
}
}
}
================================================
FILE: LinqSpecs/TrueSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs
{
/// <summary>
/// Specification which is satisfied by any object.
/// </summary>
public class TrueSpecification<T> : Specification<T>
{
/// <summary>
/// Returns an expression that defines this query.
/// </summary>
public override Expression<Func<T, bool>> ToExpression()
{
return x => true;
}
/// <summary>
/// Determines whether the specified object is equal to the current object.
/// </summary>
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return GetType() == other.GetType();
}
/// <summary>
/// Returns a hash code for the current object.
/// </summary>
public override int GetHashCode()
{
return HashCodeHelpers.Combine(GetType());
}
}
}
================================================
FILE: LinqSpecs/Utilities/ExpressionExtensions.cs
================================================
using System;
using System.Linq;
using System.Linq.Expressions;
namespace LinqSpecs.Utilities
{
// ------------------------------------------------------------------------------------------
// This code was taken from the MSDN Blog meek: LINQ to Entities: Combining Predicates
// http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
// ------------------------------------------------------------------------------------------
internal static class ExpressionExtensions
{
public static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
public static Expression<Func<T, bool>> OrElse<T>(
this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
private static Expression<T> Compose<T>(
this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with parameters from the first
var secondBody = ExpressionParameterRebinder.ReplaceParameters(map, second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
}
}
================================================
FILE: LinqSpecs/Utilities/ExpressionParameterRebinder.cs
================================================
using System.Collections.Generic;
using System.Linq.Expressions;
namespace LinqSpecs.Utilities
{
// ------------------------------------------------------------------------------------------
// This code was taken from the MSDN Blog meek: LINQ to Entities: Combining Predicates
// http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
// ------------------------------------------------------------------------------------------
internal class ExpressionParameterRebinder : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> _map;
public ExpressionParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
_map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(
Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ExpressionParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
if (_map.TryGetValue(p, out ParameterExpression? replacement))
p = replacement;
return base.VisitParameter(p);
}
}
}
================================================
FILE: LinqSpecs/Utilities/HashCodeHelpers.cs
================================================
using System;
namespace LinqSpecs.Utilities
{
internal static class HashCodeHelpers
{
public static int Combine<T>(T value)
{
#if NETSTANDARD2_1_OR_GREATER
return HashCode.Combine(value);
#else
unchecked
{
int hash = 17;
hash = hash * 23 + value?.GetHashCode() ?? 0;
return hash;
}
#endif
}
public static int Combine<T1, T2>(T1 value1, T2 value2)
{
#if NETSTANDARD2_1_OR_GREATER
return HashCode.Combine(value1, value2);
#else
unchecked
{
int hash = 17;
hash = hash * 23 + value1?.GetHashCode() ?? 0;
hash = hash * 23 + value2?.GetHashCode() ?? 0;
return hash;
}
#endif
}
public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
{
#if NETSTANDARD2_1_OR_GREATER
return HashCode.Combine(value1, value2, value3);
#else
unchecked
{
int hash = 17;
hash = hash * 23 + value1?.GetHashCode() ?? 0;
hash = hash * 23 + value2?.GetHashCode() ?? 0;
hash = hash * 23 + value3?.GetHashCode() ?? 0;
return hash;
}
#endif
}
}
}
================================================
FILE: LinqSpecs.DatabaseTests/DomainModel/Customer.cs
================================================
using System.Collections.Generic;
namespace LinqSpecs.DatabaseTests
{
public class Customer
{
public Customer(string name)
{
Name = name;
}
public int Id { get; private set; }
public string Name { get; set; }
public virtual ICollection<Order> Orders { get; } = new HashSet<Order>();
}
}
================================================
FILE: LinqSpecs.DatabaseTests/DomainModel/Order.cs
================================================
namespace LinqSpecs.DatabaseTests
{
public class Order
{
public Order(string product, int price)
{
Product = product;
Price = price;
}
public int Id { get; private set; }
public virtual Customer? Customer { get; set; }
public string Product { get; set; }
public int Price { get; set; }
}
}
================================================
FILE: LinqSpecs.DatabaseTests/DomainModel/SampleDbContext.cs
================================================
using Microsoft.EntityFrameworkCore;
namespace LinqSpecs.DatabaseTests
{
class SampleDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; } = null!;
public DbSet<Order> Orders { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=file::memory:?cache=shared");
}
}
}
================================================
FILE: LinqSpecs.DatabaseTests/LinqSpecs.DatabaseTests.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net70</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageReference Include="nunit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LinqSpecs\LinqSpecs.csproj" />
</ItemGroup>
</Project>
================================================
FILE: LinqSpecs.DatabaseTests/Tests.cs
================================================
using System.Linq;
using NUnit.Framework;
namespace LinqSpecs.DatabaseTests
{
public class Tests
{
[OneTimeSetUp]
public void SetUp()
{
var customer0 = CreateCustomer("Orderless Customer");
var customer1 = CreateCustomer("Single-order Customer", orderPrices: new[] { 100 });
var customer2 = CreateCustomer("Double-order Customer", orderPrices: new[] { 100, 200 });
var customer3 = CreateCustomer("Triple-order Customer", orderPrices: new[] { 100, 200, 300 });
using var db = new SampleDbContext();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
db.Customers.AddRange(customer0, customer1, customer2, customer3);
db.SaveChanges();
}
[OneTimeTearDown]
public void TearDown()
{
using var db = new SampleDbContext();
db.Database.EnsureDeleted();
}
[Test]
public void NoSpecification()
{
using var db = new SampleDbContext();
var customers = db.Customers.ToList();
Assert.That(customers, Has.Count.EqualTo(4));
}
[Test]
public void SimplePropertySpecification()
{
using var db = new SampleDbContext();
var nameSpec = new AdHocSpecification<Customer>(x => x.Name.StartsWith("Single"));
var customers = db.Customers.Where(nameSpec).Select(x => x.Name).ToList();
Assert.That(customers, Is.EquivalentTo(new[] { "Single-order Customer" }));
}
[Test]
public void NavigationPropertySpecification()
{
using var db = new SampleDbContext();
var hasOrdersSpec = new AdHocSpecification<Customer>(x => x.Orders.Any());
var customers = db.Customers.Where(hasOrdersSpec).Select(x => x.Name).ToList();
Assert.That(
customers,
Is.EquivalentTo(new[]
{
"Single-order Customer",
"Double-order Customer",
"Triple-order Customer"
})
);
}
[Test]
public void СombinedSpecification()
{
using var db = new SampleDbContext();
var hasMultipleOrdersSpec = new AdHocSpecification<Customer>(x => x.Orders.Count() > 1);
var highTotalPriceSpec = new AdHocSpecification<Customer>(x => x.Orders.Sum(y => y.Price) > 500);
var vipSpec = hasMultipleOrdersSpec && highTotalPriceSpec;
var customers = db.Customers.Where(vipSpec).Select(x => x.Name).ToList();
Assert.That(customers, Is.EquivalentTo(new[] { "Triple-order Customer" }));
}
private static Customer CreateCustomer(string name, params int[] orderPrices)
{
var customer = new Customer(name);
foreach (var orderPrice in orderPrices)
customer.Orders.Add(new Order("Some Product", orderPrice));
return customer;
}
}
}
================================================
FILE: LinqSpecs.UnitTests/AdHocSpecificationTests.cs
================================================
using System;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class AdHocSpecificationTests
{
[Test]
public void Constructor_should_throw_exception_when_argument_is_null()
{
Assert.Throws<ArgumentNullException>(() => new AdHocSpecification<string>(null!));
}
[Test]
public void Predicate_should_work()
{
var specification = new AdHocSpecification<string>(n => n.StartsWith("J"));
var result = new SampleRepository().Find(specification);
CollectionAssert.Contains(result, "Jose");
CollectionAssert.Contains(result, "Julian");
CollectionAssert.DoesNotContain(result, "Manuel");
}
[Test]
public void Equals_returns_true_when_another_specification_has_identical_predicate()
{
var spec1 = new AdHocSpecification<string>(x => x.Length > 1 && x.StartsWith("J"));
var spec2 = new AdHocSpecification<string>(x => x.Length > 1 && x.StartsWith("J"));
Assert.IsTrue(spec1.Equals(spec1));
Assert.IsTrue(spec1.Equals(spec2));
}
[Test]
public void Equals_returns_false_when_another_specification_has_different_predicate()
{
var spec1 = new AdHocSpecification<string>(x => x.Length > 1);
var spec2 = new AdHocSpecification<string>(x => x.Length > 2);
Assert.IsFalse(spec1.Equals(spec2));
}
[Test]
public void Equals_returns_false_when_other_specification_has_different_type()
{
var spec = new AdHocSpecification<string>(x => true);
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(new AdHocSpecification<int>(x => true)));
Assert.IsFalse(spec.Equals(new AdHocSpecification<object>(x => true)));
Assert.IsFalse(spec.Equals(new TrueSpecification<string>()));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var spec1 = new AdHocSpecification<string>(x => x.Length > 1);
var spec2 = new AdHocSpecification<string>(x => x.Length > 1);
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/AssemblyTests.cs
================================================
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class AssemblyTests
{
[Test]
public void Assembly_should_have_strong_name()
{
string? assemblyName = typeof(Specification<>).Assembly.FullName;
Assert.That(assemblyName, Does.StartWith("LinqSpecs,"));
Assert.That(assemblyName, Does.EndWith("PublicKeyToken=db098e6f22bae212"));
}
}
}
================================================
FILE: LinqSpecs.UnitTests/FalseSpecificationTests.cs
================================================
using System;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class FalseSpecificationTests
{
[Test]
public void Should_work()
{
var spec = new FalseSpecification<string>();
var result = new SampleRepository().Find(spec);
CollectionAssert.IsEmpty(result);
}
[Test]
public void Equals_returns_true_when_both_sides_are_equals()
{
var spec1 = new FalseSpecification<string>();
var spec2 = new FalseSpecification<string>();
Assert.IsTrue(spec1.Equals(spec2));
}
[Test]
public void Equals_returns_false_when_both_sides_are_not_equals()
{
var spec = new FalseSpecification<string>();
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(new AdHocSpecification<string>(x => true)));
Assert.IsFalse(spec.Equals(new FalseSpecification<object>()));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var spec1 = new FalseSpecification<string>();
var spec2 = new FalseSpecification<string>();
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/Helpers/SampleRepository.cs
================================================
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace LinqSpecs.Tests
{
public class SampleRepository : ReadOnlyCollection<string>
{
public SampleRepository()
: base(new[] { "Jose", "Manuel", "Julian" })
{ }
public IEnumerable<string> Find(Specification<string> specfication)
{
return this.AsQueryable().Where(specfication.ToExpression());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/LinqSpecs.UnitTests.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net70</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../LinqSpecs.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LinqSpecs\LinqSpecs.csproj" />
</ItemGroup>
</Project>
================================================
FILE: LinqSpecs.UnitTests/Operators/AndSpecificationTests.cs
================================================
using System;
using System.Collections.Generic;
using LinqSpecs.Operators;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
//Note; no matter if you are using & operator, or && operator.. both works as an &&.
[TestFixture]
public class AndSpecificationTests
{
[Test]
public void Constructor_should_throw_exception_when_argument_is_null()
{
var spec = new AdHocSpecification<string>(x => x.Length == 1);
Assert.Throws<ArgumentNullException>(() => new AndSpecification<string>(spec, null!));
Assert.Throws<ArgumentNullException>(() => new AndSpecification<string>(null!, spec));
}
[Test]
public void And_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var endsWithE = new AdHocSpecification<string>(n => n.EndsWith("e"));
var specfication = new AndSpecification<string>(startWithJ, endsWithE);
IEnumerable<string> result = new SampleRepository().Find(specfication);
CollectionAssert.Contains(result, "Jose");
CollectionAssert.DoesNotContain(result, "Julian");
CollectionAssert.DoesNotContain(result, "Manuel");
}
[Test]
public void Equals_returns_true_when_both_sides_are_equals()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var spec = s1 & s2;
Assert.IsInstanceOf<AndSpecification<string>>(spec);
Assert.IsTrue(spec.Equals(spec));
Assert.IsTrue(spec.Equals(s1 & s2));
Assert.IsTrue(spec.Equals(s1 && s2)); // & or && both operators behave as &&
}
[Test]
public void Equals_returns_false_when_both_sides_are_not_equals()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var s3 = new AdHocSpecification<string>(x => x.Length > 3);
var spec = s1 & s2;
Assert.IsInstanceOf<AndSpecification<string>>(spec);
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(s1));
Assert.IsFalse(spec.Equals(s2));
Assert.IsFalse(spec.Equals(s2 & s1)); // AndAlso is not commutable
Assert.IsFalse(spec.Equals(s1 & s3));
Assert.IsFalse(spec.Equals(s3 & s2));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var s3 = new AdHocSpecification<string>(x => x.Length > 3);
var spec1 = s1 & s2 & s3;
var spec2 = s1 & s2 & s3;
Assert.IsInstanceOf<AndSpecification<string>>(spec1);
Assert.IsInstanceOf<AndSpecification<string>>(spec2);
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/Operators/NotSpecificationTests.cs
================================================
using System;
using LinqSpecs.Operators;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class NotSpecificationTests
{
[Test]
public void Constructor_should_throw_exception_when_argument_is_null()
{
Assert.Throws<ArgumentNullException>(() => new NotSpecification<string>(null!));
}
[Test]
public void Negate_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var specification = new NotSpecification<string>(startWithJ);
var result = new SampleRepository().Find(specification);
CollectionAssert.DoesNotContain(result, "Jose");
CollectionAssert.DoesNotContain(result, "Julian");
CollectionAssert.Contains(result, "Manuel");
}
[Test]
public void Equals_return_true_when_the_negated_spec_are_equals()
{
var sourceSpec = new AdHocSpecification<string>(x => x.Length > 1);
var spec = !sourceSpec;
Assert.IsInstanceOf<NotSpecification<string>>(spec);
Assert.IsTrue(spec.Equals(spec));
Assert.IsTrue(spec.Equals(!sourceSpec));
}
[Test]
public void Equals_return_false_when_the_negated_spec_are_not_equals()
{
var sourceSpec1 = new AdHocSpecification<string>(x => x.Length > 1);
var sourceSpec2 = new AdHocSpecification<string>(x => x.Length > 2);
var spec = !sourceSpec1;
Assert.IsInstanceOf<NotSpecification<string>>(spec);
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(sourceSpec1));
Assert.IsFalse(spec.Equals(sourceSpec2));
Assert.IsFalse(spec.Equals(!sourceSpec2));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var sourceSpec = new AdHocSpecification<string>(x => x.Length > 0);
var spec1 = !sourceSpec;
var spec2 = !sourceSpec;
Assert.IsInstanceOf<NotSpecification<string>>(spec1);
Assert.IsInstanceOf<NotSpecification<string>>(spec2);
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/Operators/OrSpecificationTests.cs
================================================
using System;
using LinqSpecs.Operators;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class OrSpecificationTests
{
[Test]
public void Constructor_should_throw_exception_when_argument_is_null()
{
var spec = new AdHocSpecification<string>(x => x.Length == 1);
Assert.Throws<ArgumentNullException>(() => new OrSpecification<string>(spec, null!));
Assert.Throws<ArgumentNullException>(() => new OrSpecification<string>(null!, spec));
}
[Test]
public void Or_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var endsWithN = new AdHocSpecification<string>(n => n.EndsWith("n"));
var result = new SampleRepository()
.Find(new OrSpecification<string>(startWithJ, endsWithN));
CollectionAssert.Contains(result, "Jose");
CollectionAssert.Contains(result, "Julian");
CollectionAssert.DoesNotContain(result, "Manuel");
}
[Test]
public void Equals_returns_true_when_both_sides_are_equals()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var spec = s1 | s2;
Assert.IsInstanceOf<OrSpecification<string>>(spec);
Assert.IsTrue(spec.Equals(spec));
Assert.IsTrue(spec.Equals(s1 | s2));
Assert.IsTrue(spec.Equals(s1 || s2)); // | or || both operators behave as ||
}
[Test]
public void Equals_returns_false_when_both_sides_are_not_equals()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var s3 = new AdHocSpecification<string>(x => x.Length > 3);
var spec = s1 | s2;
Assert.IsInstanceOf<OrSpecification<string>>(spec);
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(s1));
Assert.IsFalse(spec.Equals(s2));
Assert.IsFalse(spec.Equals(s2 | s1)); // OrElse is not commutable
Assert.IsFalse(spec.Equals(s1 | s3));
Assert.IsFalse(spec.Equals(s3 | s2));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var s1 = new AdHocSpecification<string>(x => x.Length > 1);
var s2 = new AdHocSpecification<string>(x => x.Length > 2);
var s3 = new AdHocSpecification<string>(x => x.Length > 3);
var spec1 = s1 | s2 | s3;
var spec2 = s1 | s2 | s3;
Assert.IsInstanceOf<OrSpecification<string>>(spec1);
Assert.IsInstanceOf<OrSpecification<string>>(spec2);
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.UnitTests/SpecificationTests.cs
================================================
using System;
using System.Linq.Expressions;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class SpecificationTests
{
[Test]
public void Implicit_operator_should_convert_specification_to_expression()
{
Specification<string> spec = new AdHocSpecification<string>(s => s.Length == 2);
Expression<Func<string, bool>> expr = spec;
Assert.IsTrue(expr.Compile().Invoke("ab"));
Assert.IsFalse(expr.Compile().Invoke("abcd"));
}
[Test]
public void Implicit_operator_should_throw_exception_when_argument_is_null()
{
Specification<string> spec = null!;
Assert.Throws<ArgumentNullException>(() =>
{
Expression<Func<string, bool>> expr = spec;
});
}
[Test]
public void And_operator_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var endsWithE = new AdHocSpecification<string>(n => n.EndsWith("e"));
var result = new SampleRepository().Find(startWithJ & endsWithE);
CollectionAssert.Contains(result, "Jose");
CollectionAssert.DoesNotContain(result, "Julian");
CollectionAssert.DoesNotContain(result, "Manuel");
}
[Test]
public void Or_operator_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var endsWithN = new AdHocSpecification<string>(n => n.EndsWith("n"));
var result = new SampleRepository().Find(startWithJ | endsWithN);
CollectionAssert.Contains(result, "Jose");
CollectionAssert.Contains(result, "Julian");
CollectionAssert.DoesNotContain(result, "Manuel");
}
[Test]
public void Negate_operator_should_work()
{
var startWithJ = new AdHocSpecification<string>(n => n.StartsWith("J"));
var result = new SampleRepository().Find(!startWithJ);
CollectionAssert.DoesNotContain(result, "Jose");
CollectionAssert.DoesNotContain(result, "Julian");
CollectionAssert.Contains(result, "Manuel");
}
[Test]
public void AndAlso_operator_is_equivalent_to_And_operator()
{
var spec1 = new AdHocSpecification<string>(n => n.Length > 2);
var spec2 = new AdHocSpecification<string>(n => n.Length < 5);
Assert.AreEqual(spec1 & spec2, spec1 && spec2);
}
[Test]
public void OrElse_operator_is_equivalent_to_Or_operator()
{
var spec1 = new AdHocSpecification<string>(n => n.Length < 2);
var spec2 = new AdHocSpecification<string>(n => n.Length > 5);
Assert.AreEqual(spec1 | spec2, spec1 || spec2);
}
[Test]
public void Combination_of_boolean_operators_should_work()
{
var startWithM = new AdHocSpecification<string>(n => n.StartsWith("M"));
var endsWithN = new AdHocSpecification<string>(n => n.EndsWith("n"));
var containsU = new AdHocSpecification<string>(n => n.Contains("u"));
var result = new SampleRepository().Find(startWithM | (!endsWithN & !containsU));
CollectionAssert.Contains(result, "Jose");
CollectionAssert.DoesNotContain(result, "Julian");
CollectionAssert.Contains(result, "Manuel");
}
}
}
================================================
FILE: LinqSpecs.UnitTests/TrueSpecificationTests.cs
================================================
using System;
using NUnit.Framework;
namespace LinqSpecs.Tests
{
[TestFixture]
public class TrueSpecificationTests
{
[Test]
public void Should_work()
{
var spec = new TrueSpecification<string>();
var result = new SampleRepository().Find(spec);
CollectionAssert.Contains(result, "Jose");
CollectionAssert.Contains(result, "Julian");
CollectionAssert.Contains(result, "Manuel");
}
[Test]
public void Equals_returns_true_when_both_sides_are_equals()
{
var spec1 = new TrueSpecification<string>();
var spec2 = new TrueSpecification<string>();
Assert.IsTrue(spec1.Equals(spec2));
}
[Test]
public void Equals_returns_false_when_both_sides_are_not_equals()
{
var spec = new TrueSpecification<string>();
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(new AdHocSpecification<string>(x => true)));
Assert.IsFalse(spec.Equals(new TrueSpecification<object>()));
Assert.IsFalse(spec.Equals(null!));
}
[Test]
public void GetHashCode_retuns_same_value_for_equal_specifications()
{
var spec1 = new TrueSpecification<string>();
var spec2 = new TrueSpecification<string>();
Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());
}
}
}
================================================
FILE: LinqSpecs.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinqSpecs", "LinqSpecs\LinqSpecs.csproj", "{D60875A3-0DE2-409D-86A0-F540224D054C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinqSpecs.UnitTests", "LinqSpecs.UnitTests\LinqSpecs.UnitTests.csproj", "{0FBC6204-F18A-4D96-9C5E-39F925E7427C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinqSpecs.DatabaseTests", "LinqSpecs.DatabaseTests\LinqSpecs.DatabaseTests.csproj", "{B269AA58-028E-4BA7-ADBC-3848A4018CAE}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F7AD41A5-AE94-4151-9527-13EC4FFA18C8}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D60875A3-0DE2-409D-86A0-F540224D054C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D60875A3-0DE2-409D-86A0-F540224D054C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D60875A3-0DE2-409D-86A0-F540224D054C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D60875A3-0DE2-409D-86A0-F540224D054C}.Release|Any CPU.Build.0 = Release|Any CPU
{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Release|Any CPU.Build.0 = Release|Any CPU
{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B57D1D0-447D-4C30-A33C-51CA138160CE}
EndGlobalSection
EndGlobal
================================================
FILE: README.md
================================================

LinqSpecs is a framework that will help you to create specifications for LINQ queries that can be executed by a remote server. You can read more about the specification pattern in [Wikipedia](http://en.wikipedia.org/wiki/Specification_pattern).
Almost all users of LINQ create specifications in their daily work, but most of them write those specifications scattered all over the code. The idea behind this project is to help the user to write, test and expose specifications as first-class objects. You will learn how to use LinqSpecs in this brief document.
[](https://nuget.org/packages/LinqSpecs)
[](https://nuget.org/packages/LinqSpecs)
### Defining simple specifications
In order to define our first specification named "CustomerFromCountrySpec" we need to inherit from Specification\<T\>:
```csharp
public abstract class Specification<T>
{
public abstract Expression<Func<T, bool>> ToExpression();
}
```
So this is our implementation:
```csharp
using LinqSpecs;
public enum Country { Argentina, France, Italia, ... }
public class CustomerFromCountrySpec : Specification<Customer>
{
public Country Country { get; set; }
public CustomerFromCountrySpec(Country country)
{
Country = country;
}
public override Expression<Func<Customer, bool>> ToExpression()
{
return c => c.Country == Country;
}
}
```
Simple as is, to use this class, your repository or DAO should implement these kind of methods:
```csharp
public IEnumerable<T> Find(Specification<T> specification)
{
return [a queryable source].Where(specification).ToList();
}
public int Count(Specification<T> specification)
{
return [a queryable source].Count(specification);
}
```
The usage is very simple:
```csharp
var spec = new CustomerFromCountrySpec(Country.Argentina);
var customersFromArgentina = customerRepository.Find(spec);
```
### Alternative way to expose specifications
An alternative way of exposing specifications is with a static class:
```csharp
public static class CustomerSpecs
{
public static Specification<Customer> FromCountry(Country country)
{
return new CustomerFromCountrySpec(country);
}
public static Specification<Customer> EligibleForDiscount(decimal discount)
{
return new AdHocSpecification<Customer>(
c => c.IsPreferred && !c.HasDebt &&
c.LastPurchaseDate > DateTime.Today.AddDays(-30));
}
}
```
Usage:
```csharp
customerRepository.Find(
CustomerSpecs.FromCountry(argentina) &&
CustomerSpecs.EligibleForDiscount(3223));
```
### Logical operations AND, OR, NOT
One of the most interesting features of LinqSpecs is that you can combine known specifications with "!", "&&" and "||" operators. For example:
```csharp
var spec1 = new CustomerFromCountrySpec(Country.Argentina);
var spec2 = new CustomerPreferredSpec();
var result = customerRepository.Find(spec1 && !spec2);
```
This code returns all customers from Argentina that are not preferred. The & operator work as an && (AndAlso) operator. The same for | and ||.
### Comparing
The result of and'ing, or'ing and negating specifications implements equality members. That's said:
```csharp
// This returns true
(spec1 && spec2).Equals(spec1 && spec2);
// This returns true
(spec1 && (spec2 || !spec3)).Equals(spec1 && (spec2 || !spec3));
// This returns false, because AndAlso and OrElse are not commutable operations
(spec1 && spec2).Equals(spec2 && spec1);
```
This is an useful feature when you are writing Asserts in your unit tests.
### AdHocSpecification
The AdHocSpecification is an alternative way to write a specification without writing a class. You should not abuse of them, and try to write those in a single place as explained above.
```csharp
var spec = new AdHocSpecification<Customer>(c => c.IsPreferred && !c.HasDebt);
```
### TrueSpecification and FalseSpecification
The TrueSpecification is satisfied by any object. The FalseSpecification is not satisfied by any object.
```csharp
// This returns all customers
customerRepository.Find(new TrueSpecification<Customer>());
// This returns nothing
customerRepository.Find(new FalseSpecification<Customer>());
```
These specifications can be useful when you want to retrieve all items from a data source or when you are building a chain of several specifications. For example:
```csharp
Specification<Customer> spec = new FalseSpecification<Customer>();
foreach (var country in countries)
spec |= new CustomerFromCountrySpec(country);
return spec;
```
### In-memory queries
Although LinqSpecs is targeted towards IQueryable\<T\> data source, it is possible to use LinqSpecs specifications for filtering IEnumerable\<T\> collections and also for other checks in memory:
```csharp
IEnumerable<Customer> customers = ...
var spec = new CustomerFromCountrySpec(Country.Argentina);
var result = customers.Where(spec.ToExpression().Compile());
```
Compiling of expression tree into a delegate is a very slow operation, so it's a good idea to cache the result of a compilation for reuse if it's possible.
# Supported platforms
- .NET Standard 2.0+
- .NET Framework 4.0+
- .NET Core 2.0+
# License
LinqSpecs is open-sourced software licensed under the [Microsoft Public License (MS-PL)](https://opensource.org/licenses/MS-PL).
# Contributors
LinqSpecs was created by [José F. Romaniello](https://github.com/jfromaniello) and [Sergey Navozenko](https://github.com/navozenko).
================================================
FILE: license.txt
================================================
Microsoft Public License (Ms-PL)
This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
1. Definitions
The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the software.
A "contributor" is any person that distributes its contribution under this license.
"Licensed patents" are a contributor's patent claims that read directly on its contribution.
2. Grant of Rights
(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
3. Conditions and Limitations
(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
gitextract_i9vq50lx/ ├── .editorconfig ├── .gitattributes ├── .gitignore ├── LinqSpecs/ │ ├── AdHocSpecification.cs │ ├── FalseSpecification.cs │ ├── LinqSpecs.csproj │ ├── Operators/ │ │ ├── AndSpecification.cs │ │ ├── NotSpecification.cs │ │ └── OrSpecification.cs │ ├── Specification.cs │ ├── TrueSpecification.cs │ └── Utilities/ │ ├── ExpressionExtensions.cs │ ├── ExpressionParameterRebinder.cs │ └── HashCodeHelpers.cs ├── LinqSpecs.DatabaseTests/ │ ├── DomainModel/ │ │ ├── Customer.cs │ │ ├── Order.cs │ │ └── SampleDbContext.cs │ ├── LinqSpecs.DatabaseTests.csproj │ └── Tests.cs ├── LinqSpecs.UnitTests/ │ ├── AdHocSpecificationTests.cs │ ├── AssemblyTests.cs │ ├── FalseSpecificationTests.cs │ ├── Helpers/ │ │ └── SampleRepository.cs │ ├── LinqSpecs.UnitTests.csproj │ ├── Operators/ │ │ ├── AndSpecificationTests.cs │ │ ├── NotSpecificationTests.cs │ │ └── OrSpecificationTests.cs │ ├── SpecificationTests.cs │ └── TrueSpecificationTests.cs ├── LinqSpecs.sln ├── LinqSpecs.snk ├── README.md └── license.txt
SYMBOL INDEX (105 symbols across 23 files)
FILE: LinqSpecs.DatabaseTests/DomainModel/Customer.cs
class Customer (line 5) | public class Customer
method Customer (line 7) | public Customer(string name)
FILE: LinqSpecs.DatabaseTests/DomainModel/Order.cs
class Order (line 3) | public class Order
method Order (line 5) | public Order(string product, int price)
FILE: LinqSpecs.DatabaseTests/DomainModel/SampleDbContext.cs
class SampleDbContext (line 5) | class SampleDbContext : DbContext
method OnConfiguring (line 10) | protected override void OnConfiguring(DbContextOptionsBuilder optionsB...
FILE: LinqSpecs.DatabaseTests/Tests.cs
class Tests (line 6) | public class Tests
method SetUp (line 8) | [OneTimeSetUp]
method TearDown (line 23) | [OneTimeTearDown]
method NoSpecification (line 30) | [Test]
method SimplePropertySpecification (line 38) | [Test]
method NavigationPropertySpecification (line 49) | [Test]
method СombinedSpecification (line 68) | [Test]
method CreateCustomer (line 81) | private static Customer CreateCustomer(string name, params int[] order...
FILE: LinqSpecs.UnitTests/AdHocSpecificationTests.cs
class AdHocSpecificationTests (line 6) | [TestFixture]
method Constructor_should_throw_exception_when_argument_is_null (line 9) | [Test]
method Predicate_should_work (line 15) | [Test]
method Equals_returns_true_when_another_specification_has_identical_predicate (line 27) | [Test]
method Equals_returns_false_when_another_specification_has_different_predicate (line 37) | [Test]
method Equals_returns_false_when_other_specification_has_different_type (line 46) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 58) | [Test]
FILE: LinqSpecs.UnitTests/AssemblyTests.cs
class AssemblyTests (line 5) | [TestFixture]
method Assembly_should_have_strong_name (line 8) | [Test]
FILE: LinqSpecs.UnitTests/FalseSpecificationTests.cs
class FalseSpecificationTests (line 6) | [TestFixture]
method Should_work (line 9) | [Test]
method Equals_returns_true_when_both_sides_are_equals (line 19) | [Test]
method Equals_returns_false_when_both_sides_are_not_equals (line 28) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 39) | [Test]
FILE: LinqSpecs.UnitTests/Helpers/SampleRepository.cs
class SampleRepository (line 7) | public class SampleRepository : ReadOnlyCollection<string>
method SampleRepository (line 9) | public SampleRepository()
method Find (line 13) | public IEnumerable<string> Find(Specification<string> specfication)
FILE: LinqSpecs.UnitTests/Operators/AndSpecificationTests.cs
class AndSpecificationTests (line 10) | [TestFixture]
method Constructor_should_throw_exception_when_argument_is_null (line 13) | [Test]
method And_should_work (line 22) | [Test]
method Equals_returns_true_when_both_sides_are_equals (line 36) | [Test]
method Equals_returns_false_when_both_sides_are_not_equals (line 49) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 67) | [Test]
FILE: LinqSpecs.UnitTests/Operators/NotSpecificationTests.cs
class NotSpecificationTests (line 7) | [TestFixture]
method Constructor_should_throw_exception_when_argument_is_null (line 10) | [Test]
method Negate_should_work (line 16) | [Test]
method Equals_return_true_when_the_negated_spec_are_equals (line 29) | [Test]
method Equals_return_false_when_the_negated_spec_are_not_equals (line 40) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 55) | [Test]
FILE: LinqSpecs.UnitTests/Operators/OrSpecificationTests.cs
class OrSpecificationTests (line 7) | [TestFixture]
method Constructor_should_throw_exception_when_argument_is_null (line 10) | [Test]
method Or_should_work (line 19) | [Test]
method Equals_returns_true_when_both_sides_are_equals (line 33) | [Test]
method Equals_returns_false_when_both_sides_are_not_equals (line 46) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 64) | [Test]
FILE: LinqSpecs.UnitTests/SpecificationTests.cs
class SpecificationTests (line 7) | [TestFixture]
method Implicit_operator_should_convert_specification_to_expression (line 10) | [Test]
method Implicit_operator_should_throw_exception_when_argument_is_null (line 20) | [Test]
method And_operator_should_work (line 31) | [Test]
method Or_operator_should_work (line 44) | [Test]
method Negate_operator_should_work (line 57) | [Test]
method AndAlso_operator_is_equivalent_to_And_operator (line 69) | [Test]
method OrElse_operator_is_equivalent_to_Or_operator (line 78) | [Test]
method Combination_of_boolean_operators_should_work (line 87) | [Test]
FILE: LinqSpecs.UnitTests/TrueSpecificationTests.cs
class TrueSpecificationTests (line 6) | [TestFixture]
method Should_work (line 9) | [Test]
method Equals_returns_true_when_both_sides_are_equals (line 21) | [Test]
method Equals_returns_false_when_both_sides_are_not_equals (line 30) | [Test]
method GetHashCode_retuns_same_value_for_equal_specifications (line 41) | [Test]
FILE: LinqSpecs/AdHocSpecification.cs
class AdHocSpecification (line 10) | public class AdHocSpecification<T> : Specification<T>
method AdHocSpecification (line 19) | public AdHocSpecification(Expression<Func<T, bool>> predicate)
method ToExpression (line 28) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 36) | public override bool Equals(object? other)
method GetHashCode (line 50) | public override int GetHashCode()
FILE: LinqSpecs/FalseSpecification.cs
class FalseSpecification (line 10) | public class FalseSpecification<T> : Specification<T>
method ToExpression (line 15) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 23) | public override bool Equals(object? other)
method GetHashCode (line 35) | public override int GetHashCode()
FILE: LinqSpecs/Operators/AndSpecification.cs
class AndSpecification (line 10) | public class AndSpecification<T> : Specification<T>
method AndSpecification (line 15) | public AndSpecification(Specification<T> left, Specification<T> right)
method ToExpression (line 21) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 28) | public override bool Equals(object? other)
method GetHashCode (line 39) | public override int GetHashCode()
FILE: LinqSpecs/Operators/NotSpecification.cs
class NotSpecification (line 10) | public class NotSpecification<T> : Specification<T>
method NotSpecification (line 14) | public NotSpecification(Specification<T> source)
method ToExpression (line 19) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 25) | public override bool Equals(object? other)
method GetHashCode (line 36) | public override int GetHashCode()
FILE: LinqSpecs/Operators/OrSpecification.cs
class OrSpecification (line 10) | public class OrSpecification<T> : Specification<T>
method OrSpecification (line 15) | public OrSpecification(Specification<T> left, Specification<T> right)
method ToExpression (line 21) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 28) | public override bool Equals(object? other)
method GetHashCode (line 39) | public override int GetHashCode()
FILE: LinqSpecs/Specification.cs
class Specification (line 11) | public abstract class Specification<T>
method ToExpression (line 21) | public abstract Expression<Func<T, bool>> ToExpression();
FILE: LinqSpecs/TrueSpecification.cs
class TrueSpecification (line 10) | public class TrueSpecification<T> : Specification<T>
method ToExpression (line 15) | public override Expression<Func<T, bool>> ToExpression()
method Equals (line 23) | public override bool Equals(object? other)
method GetHashCode (line 35) | public override int GetHashCode()
FILE: LinqSpecs/Utilities/ExpressionExtensions.cs
class ExpressionExtensions (line 12) | internal static class ExpressionExtensions
method AndAlso (line 14) | public static Expression<Func<T, bool>> AndAlso<T>(
method OrElse (line 20) | public static Expression<Func<T, bool>> OrElse<T>(
method Compose (line 26) | private static Expression<T> Compose<T>(
FILE: LinqSpecs/Utilities/ExpressionParameterRebinder.cs
class ExpressionParameterRebinder (line 11) | internal class ExpressionParameterRebinder : ExpressionVisitor
method ExpressionParameterRebinder (line 15) | public ExpressionParameterRebinder(Dictionary<ParameterExpression, Par...
method ReplaceParameters (line 20) | public static Expression ReplaceParameters(
method VisitParameter (line 26) | protected override Expression VisitParameter(ParameterExpression p)
FILE: LinqSpecs/Utilities/HashCodeHelpers.cs
class HashCodeHelpers (line 5) | internal static class HashCodeHelpers
method Combine (line 7) | public static int Combine<T>(T value)
method Combine (line 21) | public static int Combine<T1, T2>(T1 value1, T2 value2)
method Combine (line 36) | public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (61K chars).
[
{
"path": ".editorconfig",
"chars": 354,
"preview": "# EditorConfig to support per-solution formatting\n# See: https://aka.ms/editorconfigdocs\n\nroot = true\n\n[*]\nindent_style"
},
{
"path": ".gitattributes",
"chars": 2518,
"preview": "###############################################################################\n# Set default behavior to automatically "
},
{
"path": ".gitignore",
"chars": 3138,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
},
{
"path": "LinqSpecs/AdHocSpecification.cs",
"chars": 1783,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n /// <summary>\n //"
},
{
"path": "LinqSpecs/FalseSpecification.cs",
"chars": 1106,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n /// <summary>\n //"
},
{
"path": "LinqSpecs/LinqSpecs.csproj",
"chars": 1305,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFrameworks>netstandard1.0;netstandard2.0;netstandard2.1"
},
{
"path": "LinqSpecs/Operators/AndSpecification.cs",
"chars": 1349,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n /// <summa"
},
{
"path": "LinqSpecs/Operators/NotSpecification.cs",
"chars": 1160,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n /// <summa"
},
{
"path": "LinqSpecs/Operators/OrSpecification.cs",
"chars": 1344,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n /// <summa"
},
{
"path": "LinqSpecs/Specification.cs",
"chars": 2412,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Operators;\n\nnamespace LinqSpecs\n{\n /// <summary>\n //"
},
{
"path": "LinqSpecs/TrueSpecification.cs",
"chars": 1100,
"preview": "using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n /// <summary>\n //"
},
{
"path": "LinqSpecs/Utilities/ExpressionExtensions.cs",
"chars": 1772,
"preview": "using System;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace LinqSpecs.Utilities\n{\n // ---------------"
},
{
"path": "LinqSpecs/Utilities/ExpressionParameterRebinder.cs",
"chars": 1333,
"preview": "using System.Collections.Generic;\nusing System.Linq.Expressions;\n\nnamespace LinqSpecs.Utilities\n{\n // --------------"
},
{
"path": "LinqSpecs/Utilities/HashCodeHelpers.cs",
"chars": 1349,
"preview": "using System;\n\nnamespace LinqSpecs.Utilities\n{\n internal static class HashCodeHelpers\n {\n public static in"
},
{
"path": "LinqSpecs.DatabaseTests/DomainModel/Customer.cs",
"chars": 364,
"preview": "using System.Collections.Generic;\n\nnamespace LinqSpecs.DatabaseTests\n{\n public class Customer\n {\n public C"
},
{
"path": "LinqSpecs.DatabaseTests/DomainModel/Order.cs",
"chars": 387,
"preview": "namespace LinqSpecs.DatabaseTests\n{\n public class Order\n {\n public Order(string product, int price)\n "
},
{
"path": "LinqSpecs.DatabaseTests/DomainModel/SampleDbContext.cs",
"chars": 436,
"preview": "using Microsoft.EntityFrameworkCore;\n\nnamespace LinqSpecs.DatabaseTests\n{\n class SampleDbContext : DbContext\n {\n "
},
{
"path": "LinqSpecs.DatabaseTests/LinqSpecs.DatabaseTests.csproj",
"chars": 750,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFramework>net70</TargetFramework>\n <Nullable>enable<"
},
{
"path": "LinqSpecs.DatabaseTests/Tests.cs",
"chars": 3110,
"preview": "using System.Linq;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.DatabaseTests\n{\n public class Tests\n {\n [On"
},
{
"path": "LinqSpecs.UnitTests/AdHocSpecificationTests.cs",
"chars": 2368,
"preview": "using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n public class AdHocSpecification"
},
{
"path": "LinqSpecs.UnitTests/AssemblyTests.cs",
"chars": 442,
"preview": "using NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n public class AssemblyTests\n {\n [Tes"
},
{
"path": "LinqSpecs.UnitTests/FalseSpecificationTests.cs",
"chars": 1365,
"preview": "using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n public class FalseSpecification"
},
{
"path": "LinqSpecs.UnitTests/Helpers/SampleRepository.cs",
"chars": 472,
"preview": "using System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace LinqSpecs.Tests\n{"
},
{
"path": "LinqSpecs.UnitTests/LinqSpecs.UnitTests.csproj",
"chars": 739,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFrameworks>net48;net70</TargetFrameworks>\n <IsPackab"
},
{
"path": "LinqSpecs.UnitTests/Operators/AndSpecificationTests.cs",
"chars": 3195,
"preview": "using System;\nusing System.Collections.Generic;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs."
},
{
"path": "LinqSpecs.UnitTests/Operators/NotSpecificationTests.cs",
"chars": 2358,
"preview": "using System;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n publ"
},
{
"path": "LinqSpecs.UnitTests/Operators/OrSpecificationTests.cs",
"chars": 3011,
"preview": "using System;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n publ"
},
{
"path": "LinqSpecs.UnitTests/SpecificationTests.cs",
"chars": 3553,
"preview": "using System;\nusing System.Linq.Expressions;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n "
},
{
"path": "LinqSpecs.UnitTests/TrueSpecificationTests.cs",
"chars": 1480,
"preview": "using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n [TestFixture]\n public class TrueSpecificationT"
},
{
"path": "LinqSpecs.sln",
"chars": 2362,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3012"
},
{
"path": "README.md",
"chars": 5642,
"preview": "\n\nLinqSpecs is a framework that will help you to create "
},
{
"path": "license.txt",
"chars": 2630,
"preview": "Microsoft Public License (Ms-PL)\n\nThis license governs use of the accompanying software. If you use the software, you ac"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the navozenko/LinqSpecs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (55.4 KB), approximately 13.6k tokens, and a symbol index with 105 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.