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
{
///
/// Allows to write a without writing a class.
///
public class AdHocSpecification : Specification
{
private readonly Lazy _predicateString;
public Expression> Predicate { get; }
///
/// Creates a custom ad-hoc from the given predicate expression.
///
public AdHocSpecification(Expression> predicate)
{
Predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));
_predicateString = new Lazy(() => Predicate.ToString());
}
///
/// Returns an expression that defines this query.
///
public override Expression> ToExpression()
{
return Predicate;
}
///
/// Determines whether the specified object is equal to the current object.
///
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
if (other is AdHocSpecification otherSpec)
return _predicateString.Value.Equals(otherSpec._predicateString.Value);
return false;
}
///
/// Returns a hash code for the current object.
///
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
{
///
/// Specification which is not satisfied by any object.
///
public class FalseSpecification : Specification
{
///
/// Returns an expression that defines this query.
///
public override Expression> ToExpression()
{
return x => false;
}
///
/// Determines whether the specified object is equal to the current object.
///
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return GetType() == other.GetType();
}
///
/// Returns a hash code for the current object.
///
public override int GetHashCode()
{
return HashCodeHelpers.Combine(GetType());
}
}
}
================================================
FILE: LinqSpecs/LinqSpecs.csproj
================================================
netstandard1.0;netstandard2.0;netstandard2.1
8.0
enable
3.2.0
José F. Romaniello, Sergey Navozenko
Copyright © 2010-2022
true
MS-PL
https://github.com/navozenko/LinqSpecs
linq specification spec ddd
https://github.com/navozenko/LinqSpecs
true
true
1591
true
../LinqSpecs.snk
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+
================================================
FILE: LinqSpecs/Operators/AndSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs.Operators
{
///
/// Combines two specifications by using logical AND operation.
///
public class AndSpecification : Specification
{
public Specification Left { get; }
public Specification Right { get; }
public AndSpecification(Specification left, Specification right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
}
public override Expression> 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 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
{
///
/// Negates a source specification.
///
public class NotSpecification : Specification
{
public Specification Source { get; }
public NotSpecification(Specification source)
{
Source = source ?? throw new ArgumentNullException(nameof(source));
}
public override Expression> ToExpression()
{
var expr = Source.ToExpression();
return Expression.Lambda>(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 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
{
///
/// Combines two specifications by using logical OR operation.
///
public class OrSpecification : Specification
{
public Specification Left { get; }
public Specification Right { get; }
public OrSpecification(Specification left, Specification right)
{
Left = left ?? throw new ArgumentNullException(nameof(left));
Right = right ?? throw new ArgumentNullException(nameof(right));
}
public override Expression> 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 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
{
///
/// Base class for query specifications that can be combined
/// using logical AND, OR and NOT operators.
///
public abstract class Specification
{
///
/// Returns an expression that defines this query.
///
///
/// 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.
///
public abstract Expression> ToExpression();
///
/// Performs an implicit conversion from
/// to a LINQ expression.
///
public static implicit operator Expression>(Specification spec)
{
return spec?.ToExpression() ?? throw new ArgumentNullException(nameof(spec));
}
///
/// Override operator false for supporting && and || operations
///
public static bool operator false(Specification spec)
{
return false;
}
///
/// Override operator true for supporting && and || operations
///
public static bool operator true(Specification spec)
{
return false;
}
///
/// Allows to combine two query specifications using a logical AND operation.
///
public static Specification operator &(Specification spec1, Specification spec2)
{
return new AndSpecification(spec1, spec2);
}
///
/// Allows to combine two query specifications using a logical OR operation.
///
public static Specification operator |(Specification spec1, Specification spec2)
{
return new OrSpecification(spec1, spec2);
}
///
/// Negates the given specification.
///
public static Specification operator !(Specification spec)
{
return new NotSpecification(spec);
}
}
}
================================================
FILE: LinqSpecs/TrueSpecification.cs
================================================
using System;
using System.Linq.Expressions;
using LinqSpecs.Utilities;
namespace LinqSpecs
{
///
/// Specification which is satisfied by any object.
///
public class TrueSpecification : Specification
{
///
/// Returns an expression that defines this query.
///
public override Expression> ToExpression()
{
return x => true;
}
///
/// Determines whether the specified object is equal to the current object.
///
public override bool Equals(object? other)
{
if (other is null)
return false;
if (ReferenceEquals(this, other))
return true;
return GetType() == other.GetType();
}
///
/// Returns a hash code for the current object.
///
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> AndAlso(
this Expression> first, Expression> second)
{
return first.Compose(second, Expression.AndAlso);
}
public static Expression> OrElse(
this Expression> first, Expression> second)
{
return first.Compose(second, Expression.OrElse);
}
private static Expression Compose(
this Expression first, Expression second, Func 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(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 _map;
public ExpressionParameterRebinder(Dictionary map)
{
_map = map ?? new Dictionary();
}
public static Expression ReplaceParameters(
Dictionary 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 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 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 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 Orders { get; } = new HashSet();
}
}
================================================
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 Customers { get; set; } = null!;
public DbSet Orders { get; set; } = null!;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Data Source=file::memory:?cache=shared");
}
}
}
================================================
FILE: LinqSpecs.DatabaseTests/LinqSpecs.DatabaseTests.csproj
================================================
net70
enable
false
true
================================================
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(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(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(x => x.Orders.Count() > 1);
var highTotalPriceSpec = new AdHocSpecification(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(() => new AdHocSpecification(null!));
}
[Test]
public void Predicate_should_work()
{
var specification = new AdHocSpecification(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(x => x.Length > 1 && x.StartsWith("J"));
var spec2 = new AdHocSpecification(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(x => x.Length > 1);
var spec2 = new AdHocSpecification(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(x => true);
Assert.IsFalse(spec.Equals(10));
Assert.IsFalse(spec.Equals(new AdHocSpecification(x => true)));
Assert.IsFalse(spec.Equals(new AdHocSpecification