[
  {
    "path": ".editorconfig",
    "content": "﻿# EditorConfig to support per-solution formatting\n# See: https://aka.ms/editorconfigdocs\n\nroot = true\n\n[*]\nindent_style = space\ntrim_trailing_whitespace = true\ncharset = utf-8-bom\ninsert_final_newline = true\n\n[*.{cs}]\nindent_size = 4\ndotnet_sort_system_directives_first = true\n\n[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbuild/\nbld/\n[Bb]in/\n[Oo]bj/\n\n# Visual Studio 2015 cache/options directory\n.vs/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# DNX\nproject.lock.json\nartifacts/\n\n*_i.c\n*_p.c\n*_i.h\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n## TODO: Comment the next line if you want to checkin your\n## web deploy settings but do note that will include unencrypted\n## passwords\n#*.pubxml\n\n*.publishproj\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/packages/*\n# except build/, which is used as an MSBuild target.\n!**/packages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/packages/repositories.config\n\n# Windows Azure Build Output\ncsx/\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\n# Others\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.pfx\n*.publishsettings\nnode_modules/\norleans.codegen.cs\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\n*.mdf\n*.ldf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# LightSwitch generated files\nGeneratedArtifacts/\n_Pvt_Extensions/\nModelManifest.xml\n"
  },
  {
    "path": "LinqSpecs/AdHocSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n    /// <summary>\n    /// Allows to write a <see cref=\"Specification{T}\"/> without writing a class.\n    /// </summary>\n    public class AdHocSpecification<T> : Specification<T>\n    {\n        private readonly Lazy<string> _predicateString;\n\n        public Expression<Func<T, bool>> Predicate { get; }\n\n        /// <summary>\n        /// Creates a custom ad-hoc <see cref=\"Specification{T}\"/> from the given predicate expression.\n        /// </summary>\n        public AdHocSpecification(Expression<Func<T, bool>> predicate)\n        {\n            Predicate = predicate ?? throw new ArgumentNullException(nameof(predicate));\n            _predicateString = new Lazy<string>(() => Predicate.ToString());\n        }\n\n        /// <summary>\n        /// Returns an expression that defines this query.\n        /// </summary>\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            return Predicate;\n        }\n\n        /// <summary>\n        /// Determines whether the specified object is equal to the current object.\n        /// </summary>\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            if (other is AdHocSpecification<T> otherSpec)\n                return _predicateString.Value.Equals(otherSpec._predicateString.Value);\n            return false;\n        }\n\n        /// <summary>\n        /// Returns a hash code for the current object.\n        /// </summary>\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(_predicateString.Value, GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/FalseSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n    /// <summary>\n    /// Specification which is not satisfied by any object.\n    /// </summary>\n    public class FalseSpecification<T> : Specification<T>\n    {\n        /// <summary>\n        /// Returns an expression that defines this query.\n        /// </summary>\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            return x => false;\n        }\n        \n        /// <summary>\n        /// Determines whether the specified object is equal to the current object.\n        /// </summary>\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            return GetType() == other.GetType();\n        }\n        \n        /// <summary>\n        /// Returns a hash code for the current object.\n        /// </summary>\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/LinqSpecs.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>\n    <LangVersion>8.0</LangVersion>\n    <Nullable>enable</Nullable>\n    <Version>3.2.0</Version>\n    <Authors>José F. Romaniello, Sergey Navozenko</Authors>\n    <Company></Company>\n    <Copyright>Copyright © 2010-2022</Copyright>\n    <GeneratePackageOnBuild>true</GeneratePackageOnBuild>\n    <PackageLicenseExpression>MS-PL</PackageLicenseExpression>\n    <PackageProjectUrl>https://github.com/navozenko/LinqSpecs</PackageProjectUrl>\n    <PackageTags>linq specification spec ddd</PackageTags>\n    <RepositoryUrl>https://github.com/navozenko/LinqSpecs</RepositoryUrl>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n    <NoWarn>1591</NoWarn>\n    <SignAssembly>true</SignAssembly>\n    <AssemblyOriginatorKeyFile>../LinqSpecs.snk</AssemblyOriginatorKeyFile>\n    <Description>LinqSpecs is a framework that will help you to create specifications for LINQ queries.\n\nSupported platforms:\n- .NET Standard 1.0+\n- .NET Framework 4.5+\n- .NET Core 2.0+\n- .NET 5.0+</Description>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <Content Include=\"..\\license.txt\" PackagePath=\"license.txt\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "LinqSpecs/Operators/AndSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n    /// <summary>\n    /// Combines two specifications by using logical AND operation.\n    /// </summary>\n    public class AndSpecification<T> : Specification<T>\n    {\n        public Specification<T> Left { get; }\n        public Specification<T> Right { get; }\n\n        public AndSpecification(Specification<T> left, Specification<T> right)\n        {\n            Left = left ?? throw new ArgumentNullException(nameof(left));\n            Right = right ?? throw new ArgumentNullException(nameof(right));\n        }\n\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            var expr1 = Left.ToExpression();\n            var expr2 = Right.ToExpression();\n            return expr1.AndAlso(expr2);\n        }\n\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            if (other is AndSpecification<T> otherSpec)\n                return Left.Equals(otherSpec.Left) && Right.Equals(otherSpec.Right);\n            return false;\n        }\n\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(Left, Right, GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Operators/NotSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n    /// <summary>\n    /// Negates a source specification.\n    /// </summary>\n    public class NotSpecification<T> : Specification<T>\n    {\n        public Specification<T> Source { get; }\n\n        public NotSpecification(Specification<T> source)\n        {\n            Source = source ?? throw new ArgumentNullException(nameof(source));\n        }\n\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            var expr = Source.ToExpression();\n            return Expression.Lambda<Func<T, bool>>(Expression.Not(expr.Body), expr.Parameters);\n        }\n\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            if (other is NotSpecification<T> otherSpec)\n                return Source.Equals(otherSpec.Source);\n            return false;\n        }\n\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(Source, GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Operators/OrSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs.Operators\n{\n    /// <summary>\n    /// Combines two specifications by using logical OR operation.\n    /// </summary>\n    public class OrSpecification<T> : Specification<T>\n    {\n        public Specification<T> Left { get; }\n        public Specification<T> Right { get; }\n\n        public OrSpecification(Specification<T> left, Specification<T> right)\n        {\n            Left = left ?? throw new ArgumentNullException(nameof(left));\n            Right = right ?? throw new ArgumentNullException(nameof(right));\n        }\n\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            var expr1 = Left.ToExpression();\n            var expr2 = Right.ToExpression();\n            return expr1.OrElse(expr2);\n        }\n\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            if (other is OrSpecification<T> otherSpec)\n                return Left.Equals(otherSpec.Left) && Right.Equals(otherSpec.Right);\n            return false;\n        }\n\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(Left, Right, GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Specification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Operators;\n\nnamespace LinqSpecs\n{\n    /// <summary>\n    /// Base class for query specifications that can be combined\n    /// using logical AND, OR and NOT operators.\n    /// </summary>\n    public abstract class Specification<T>\n    {\n        /// <summary>\n        /// Returns an expression that defines this query.\n        /// </summary>\n        /// <remarks>\n        /// Typically calling this method is not needed as the query\n        /// specification can be converted implicitly to an expression\n        /// by just assigning it or passing it as such to another method.\n        /// </remarks>\n        public abstract Expression<Func<T, bool>> ToExpression();\n\n        /// <summary>\n        /// Performs an implicit conversion from <see cref=\"Specification{T}\"/>\n        /// to a LINQ expression.\n        /// </summary>\n        public static implicit operator Expression<Func<T, bool>>(Specification<T> spec)\n        {\n            return spec?.ToExpression() ?? throw new ArgumentNullException(nameof(spec));\n        }\n\n        /// <summary>\n        /// Override operator false for supporting &amp;&amp; and || operations\n        /// </summary>\n        public static bool operator false(Specification<T> spec)\n        {\n            return false;\n        }\n\n        /// <summary>\n        /// Override operator true for supporting &amp;&amp; and || operations\n        /// </summary>\n        public static bool operator true(Specification<T> spec)\n        {\n            return false;\n        }\n\n        /// <summary>\n        /// Allows to combine two query specifications using a logical AND operation.\n        /// </summary>\n        public static Specification<T> operator &(Specification<T> spec1, Specification<T> spec2)\n        {\n            return new AndSpecification<T>(spec1, spec2);\n        }\n\n        /// <summary>\n        /// Allows to combine two query specifications using a logical OR operation.\n        /// </summary>\n        public static Specification<T> operator |(Specification<T> spec1, Specification<T> spec2)\n        {\n            return new OrSpecification<T>(spec1, spec2);\n        }\n\n        /// <summary>\n        /// Negates the given specification.\n        /// </summary>\n        public static Specification<T> operator !(Specification<T> spec)\n        {\n            return new NotSpecification<T>(spec);\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/TrueSpecification.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing LinqSpecs.Utilities;\n\nnamespace LinqSpecs\n{\n    /// <summary>\n    /// Specification which is satisfied by any object.\n    /// </summary>\n    public class TrueSpecification<T> : Specification<T>\n    {\n        /// <summary>\n        /// Returns an expression that defines this query.\n        /// </summary>\n        public override Expression<Func<T, bool>> ToExpression()\n        {\n            return x => true;\n        }\n        \n        /// <summary>\n        /// Determines whether the specified object is equal to the current object.\n        /// </summary>\n        public override bool Equals(object? other)\n        {\n            if (other is null)\n                return false;\n            if (ReferenceEquals(this, other))\n                return true;\n            return GetType() == other.GetType();\n        }\n        \n        /// <summary>\n        /// Returns a hash code for the current object.\n        /// </summary>\n        public override int GetHashCode()\n        {\n            return HashCodeHelpers.Combine(GetType());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Utilities/ExpressionExtensions.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Linq.Expressions;\n\nnamespace LinqSpecs.Utilities\n{\n    // ------------------------------------------------------------------------------------------\n    // This code was taken from the MSDN Blog meek: LINQ to Entities: Combining Predicates\n    // http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx\n    // ------------------------------------------------------------------------------------------\n\n    internal static class ExpressionExtensions\n    {\n        public static Expression<Func<T, bool>> AndAlso<T>(\n            this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)\n        {\n            return first.Compose(second, Expression.AndAlso);\n        }\n\n        public static Expression<Func<T, bool>> OrElse<T>(\n            this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)\n        {\n            return first.Compose(second, Expression.OrElse);\n        }\n\n        private static Expression<T> Compose<T>(\n            this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)\n        {\n            // build parameter map (from parameters of second to parameters of first)\n            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);\n\n            // replace parameters in the second lambda expression with parameters from the first\n            var secondBody = ExpressionParameterRebinder.ReplaceParameters(map, second.Body);\n\n            // apply composition of lambda expression bodies to parameters from the first expression \n            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Utilities/ExpressionParameterRebinder.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq.Expressions;\n\nnamespace LinqSpecs.Utilities\n{\n    // ------------------------------------------------------------------------------------------\n    // This code was taken from the MSDN Blog meek: LINQ to Entities: Combining Predicates\n    // http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx\n    // ------------------------------------------------------------------------------------------\n\n    internal class ExpressionParameterRebinder : ExpressionVisitor\n    {\n        private readonly Dictionary<ParameterExpression, ParameterExpression> _map;\n\n        public ExpressionParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)\n        {\n            _map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();\n        }\n\n        public static Expression ReplaceParameters(\n            Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)\n        {\n            return new ExpressionParameterRebinder(map).Visit(exp);\n        }\n\n        protected override Expression VisitParameter(ParameterExpression p)\n        {\n            if (_map.TryGetValue(p, out ParameterExpression? replacement))\n                p = replacement;\n            return base.VisitParameter(p);\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs/Utilities/HashCodeHelpers.cs",
    "content": "﻿using System;\n\nnamespace LinqSpecs.Utilities\n{\n    internal static class HashCodeHelpers\n    {\n        public static int Combine<T>(T value)\n        {\n#if NETSTANDARD2_1_OR_GREATER\n            return HashCode.Combine(value);\n#else\n            unchecked\n            {\n                int hash = 17;\n                hash = hash * 23 + value?.GetHashCode() ?? 0;\n                return hash;\n            }\n#endif\n        }\n\n        public static int Combine<T1, T2>(T1 value1, T2 value2)\n        {\n#if NETSTANDARD2_1_OR_GREATER\n            return HashCode.Combine(value1, value2);\n#else\n            unchecked\n            {\n                int hash = 17;\n                hash = hash * 23 + value1?.GetHashCode() ?? 0;\n                hash = hash * 23 + value2?.GetHashCode() ?? 0;\n                return hash;\n            }\n#endif\n        }\n\n        public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)\n        {\n#if NETSTANDARD2_1_OR_GREATER\n            return HashCode.Combine(value1, value2, value3);\n#else\n            unchecked\n            {\n                int hash = 17;\n                hash = hash * 23 + value1?.GetHashCode() ?? 0;\n                hash = hash * 23 + value2?.GetHashCode() ?? 0;\n                hash = hash * 23 + value3?.GetHashCode() ?? 0;\n                return hash;\n            }\n#endif\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.DatabaseTests/DomainModel/Customer.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace LinqSpecs.DatabaseTests\n{\n    public class Customer\n    {\n        public Customer(string name)\n        {\n            Name = name;\n        }\n\n        public int Id { get; private set; }\n\n        public string Name { get; set; }\n\n        public virtual ICollection<Order> Orders { get; } = new HashSet<Order>();\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.DatabaseTests/DomainModel/Order.cs",
    "content": "﻿namespace LinqSpecs.DatabaseTests\n{\n    public class Order\n    {\n        public Order(string product, int price)\n        {\n            Product = product;\n            Price = price;\n        }\n\n        public int Id { get; private set; }\n\n        public virtual Customer? Customer { get; set; }\n\n        public string Product { get; set; }\n\n        public int Price { get; set; }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.DatabaseTests/DomainModel/SampleDbContext.cs",
    "content": "﻿using Microsoft.EntityFrameworkCore;\n\nnamespace LinqSpecs.DatabaseTests\n{\n    class SampleDbContext : DbContext\n    {\n        public DbSet<Customer> Customers { get; set; } = null!;\n        public DbSet<Order> Orders { get; set; } = null!;\n\n        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)\n        {\n            optionsBuilder.UseSqlite(\"Data Source=file::memory:?cache=shared\");\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.DatabaseTests/LinqSpecs.DatabaseTests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net70</TargetFramework>\n    <Nullable>enable</Nullable>\n    <IsPackable>false</IsPackable>\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore\" Version=\"7.0.0\" />\n    <PackageReference Include=\"Microsoft.EntityFrameworkCore.Sqlite\" Version=\"7.0.0\" />\n    <PackageReference Include=\"nunit\" Version=\"3.13.3\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" Version=\"3.17.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.4.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LinqSpecs\\LinqSpecs.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "LinqSpecs.DatabaseTests/Tests.cs",
    "content": "﻿using System.Linq;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.DatabaseTests\n{\n    public class Tests\n    {\n        [OneTimeSetUp]\n        public void SetUp()\n        {\n            var customer0 = CreateCustomer(\"Orderless Customer\");\n            var customer1 = CreateCustomer(\"Single-order Customer\", orderPrices: new[] { 100 });\n            var customer2 = CreateCustomer(\"Double-order Customer\", orderPrices: new[] { 100, 200 });\n            var customer3 = CreateCustomer(\"Triple-order Customer\", orderPrices: new[] { 100, 200, 300 });\n\n            using var db = new SampleDbContext();\n            db.Database.EnsureDeleted();\n            db.Database.EnsureCreated();\n            db.Customers.AddRange(customer0, customer1, customer2, customer3);\n            db.SaveChanges();\n        }\n\n        [OneTimeTearDown]\n        public void TearDown()\n        {\n            using var db = new SampleDbContext();\n            db.Database.EnsureDeleted();\n        }\n\n        [Test]\n        public void NoSpecification()\n        {\n            using var db = new SampleDbContext();\n            var customers = db.Customers.ToList();\n            Assert.That(customers, Has.Count.EqualTo(4));\n        }\n\n        [Test]\n        public void SimplePropertySpecification()\n        {\n            using var db = new SampleDbContext();\n            var nameSpec = new AdHocSpecification<Customer>(x => x.Name.StartsWith(\"Single\"));\n\n            var customers = db.Customers.Where(nameSpec).Select(x => x.Name).ToList();\n\n            Assert.That(customers, Is.EquivalentTo(new[] { \"Single-order Customer\" }));\n        }\n\n        [Test]\n        public void NavigationPropertySpecification()\n        {\n            using var db = new SampleDbContext();\n            var hasOrdersSpec = new AdHocSpecification<Customer>(x => x.Orders.Any());\n\n            var customers = db.Customers.Where(hasOrdersSpec).Select(x => x.Name).ToList();\n\n            Assert.That(\n                customers,\n                Is.EquivalentTo(new[]\n                {\n                    \"Single-order Customer\",\n                    \"Double-order Customer\",\n                    \"Triple-order Customer\"\n                })\n            );\n        }\n\n        [Test]\n        public void СombinedSpecification()\n        {\n            using var db = new SampleDbContext();\n            var hasMultipleOrdersSpec = new AdHocSpecification<Customer>(x => x.Orders.Count() > 1);\n            var highTotalPriceSpec = new AdHocSpecification<Customer>(x => x.Orders.Sum(y => y.Price) > 500);\n            var vipSpec = hasMultipleOrdersSpec && highTotalPriceSpec;\n\n            var customers = db.Customers.Where(vipSpec).Select(x => x.Name).ToList();\n\n            Assert.That(customers, Is.EquivalentTo(new[] { \"Triple-order Customer\" }));\n        }\n\n        private static Customer CreateCustomer(string name, params int[] orderPrices)\n        {\n            var customer = new Customer(name);\n\n            foreach (var orderPrice in orderPrices)\n                customer.Orders.Add(new Order(\"Some Product\", orderPrice));\n\n            return customer;\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/AdHocSpecificationTests.cs",
    "content": "﻿using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class AdHocSpecificationTests\n    {\n        [Test]\n        public void Constructor_should_throw_exception_when_argument_is_null()\n        {\n            Assert.Throws<ArgumentNullException>(() => new AdHocSpecification<string>(null!));\n        }\n\n        [Test]\n        public void Predicate_should_work()\n        {\n            var specification = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n\n            var result = new SampleRepository().Find(specification);\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.Contains(result, \"Julian\");\n            CollectionAssert.DoesNotContain(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Equals_returns_true_when_another_specification_has_identical_predicate()\n        {\n            var spec1 = new AdHocSpecification<string>(x => x.Length > 1 && x.StartsWith(\"J\"));\n            var spec2 = new AdHocSpecification<string>(x => x.Length > 1 && x.StartsWith(\"J\"));\n\n            Assert.IsTrue(spec1.Equals(spec1));\n            Assert.IsTrue(spec1.Equals(spec2));\n        }\n\n        [Test]\n        public void Equals_returns_false_when_another_specification_has_different_predicate()\n        {\n            var spec1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var spec2 = new AdHocSpecification<string>(x => x.Length > 2);\n\n            Assert.IsFalse(spec1.Equals(spec2));\n        }\n\n        [Test]\n        public void Equals_returns_false_when_other_specification_has_different_type()\n        {\n            var spec = new AdHocSpecification<string>(x => true);\n\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(new AdHocSpecification<int>(x => true)));\n            Assert.IsFalse(spec.Equals(new AdHocSpecification<object>(x => true)));\n            Assert.IsFalse(spec.Equals(new TrueSpecification<string>()));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var spec1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var spec2 = new AdHocSpecification<string>(x => x.Length > 1);\n\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/AssemblyTests.cs",
    "content": "﻿using NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class AssemblyTests\n    {\n        [Test]\n        public void Assembly_should_have_strong_name()\n        {\n            string? assemblyName = typeof(Specification<>).Assembly.FullName;\n\n            Assert.That(assemblyName, Does.StartWith(\"LinqSpecs,\"));\n            Assert.That(assemblyName, Does.EndWith(\"PublicKeyToken=db098e6f22bae212\"));\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/FalseSpecificationTests.cs",
    "content": "﻿using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class FalseSpecificationTests\n    {\n        [Test]\n        public void Should_work()\n        {\n            var spec = new FalseSpecification<string>();\n\n            var result = new SampleRepository().Find(spec);\n\n            CollectionAssert.IsEmpty(result);\n        }\n\n        [Test]\n        public void Equals_returns_true_when_both_sides_are_equals()\n        {\n            var spec1 = new FalseSpecification<string>();\n            var spec2 = new FalseSpecification<string>();\n\n            Assert.IsTrue(spec1.Equals(spec2));\n        }\n\n        [Test]\n        public void Equals_returns_false_when_both_sides_are_not_equals()\n        {\n            var spec = new FalseSpecification<string>();\n\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(new AdHocSpecification<string>(x => true)));\n            Assert.IsFalse(spec.Equals(new FalseSpecification<object>()));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var spec1 = new FalseSpecification<string>();\n            var spec2 = new FalseSpecification<string>();\n\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/Helpers/SampleRepository.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace LinqSpecs.Tests\n{\n    public class SampleRepository : ReadOnlyCollection<string>\n    {\n        public SampleRepository()\n            : base(new[] { \"Jose\", \"Manuel\", \"Julian\" })\n        { }\n\n        public IEnumerable<string> Find(Specification<string> specfication)\n        {\n            return this.AsQueryable().Where(specfication.ToExpression());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/LinqSpecs.UnitTests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>net48;net70</TargetFrameworks>\n    <IsPackable>false</IsPackable>\n    <LangVersion>10.0</LangVersion>\n    <Nullable>enable</Nullable>\n    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\n    <SignAssembly>true</SignAssembly>\n    <AssemblyOriginatorKeyFile>../LinqSpecs.snk</AssemblyOriginatorKeyFile>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"17.4.0\" />\n    <PackageReference Include=\"NUnit\" Version=\"3.13.3\" />\n    <PackageReference Include=\"NUnit3TestAdapter\" Version=\"3.17.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\LinqSpecs\\LinqSpecs.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "LinqSpecs.UnitTests/Operators/AndSpecificationTests.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    //Note; no matter if you are using & operator, or && operator.. both works as an &&.\n\n    [TestFixture]\n    public class AndSpecificationTests\n    {\n        [Test]\n        public void Constructor_should_throw_exception_when_argument_is_null()\n        {\n            var spec = new AdHocSpecification<string>(x => x.Length == 1);\n\n            Assert.Throws<ArgumentNullException>(() => new AndSpecification<string>(spec, null!));\n            Assert.Throws<ArgumentNullException>(() => new AndSpecification<string>(null!, spec));\n        }\n\n        [Test]\n        public void And_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n            var endsWithE = new AdHocSpecification<string>(n => n.EndsWith(\"e\"));\n            var specfication = new AndSpecification<string>(startWithJ, endsWithE);\n\n            IEnumerable<string> result = new SampleRepository().Find(specfication);\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.DoesNotContain(result, \"Julian\");\n            CollectionAssert.DoesNotContain(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Equals_returns_true_when_both_sides_are_equals()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var spec = s1 & s2;\n\n            Assert.IsInstanceOf<AndSpecification<string>>(spec);\n            Assert.IsTrue(spec.Equals(spec));\n            Assert.IsTrue(spec.Equals(s1 & s2));\n            Assert.IsTrue(spec.Equals(s1 && s2)); // & or && both operators behave as &&\n        }\n\n        [Test]\n        public void Equals_returns_false_when_both_sides_are_not_equals()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var s3 = new AdHocSpecification<string>(x => x.Length > 3);\n            var spec = s1 & s2;\n\n            Assert.IsInstanceOf<AndSpecification<string>>(spec);\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(s1));\n            Assert.IsFalse(spec.Equals(s2));\n            Assert.IsFalse(spec.Equals(s2 & s1)); // AndAlso is not commutable\n            Assert.IsFalse(spec.Equals(s1 & s3));\n            Assert.IsFalse(spec.Equals(s3 & s2));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var s3 = new AdHocSpecification<string>(x => x.Length > 3);\n            var spec1 = s1 & s2 & s3;\n            var spec2 = s1 & s2 & s3;\n\n            Assert.IsInstanceOf<AndSpecification<string>>(spec1);\n            Assert.IsInstanceOf<AndSpecification<string>>(spec2);\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/Operators/NotSpecificationTests.cs",
    "content": "﻿using System;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class NotSpecificationTests\n    {\n        [Test]\n        public void Constructor_should_throw_exception_when_argument_is_null()\n        {\n            Assert.Throws<ArgumentNullException>(() => new NotSpecification<string>(null!));\n        }\n\n        [Test]\n        public void Negate_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n            var specification = new NotSpecification<string>(startWithJ);\n\n            var result = new SampleRepository().Find(specification);\n\n            CollectionAssert.DoesNotContain(result, \"Jose\");\n            CollectionAssert.DoesNotContain(result, \"Julian\");\n            CollectionAssert.Contains(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Equals_return_true_when_the_negated_spec_are_equals()\n        {\n            var sourceSpec = new AdHocSpecification<string>(x => x.Length > 1);\n            var spec = !sourceSpec;\n\n            Assert.IsInstanceOf<NotSpecification<string>>(spec);\n            Assert.IsTrue(spec.Equals(spec));\n            Assert.IsTrue(spec.Equals(!sourceSpec));\n        }\n\n        [Test]\n        public void Equals_return_false_when_the_negated_spec_are_not_equals()\n        {\n            var sourceSpec1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var sourceSpec2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var spec = !sourceSpec1;\n\n            Assert.IsInstanceOf<NotSpecification<string>>(spec);\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(sourceSpec1));\n            Assert.IsFalse(spec.Equals(sourceSpec2));\n            Assert.IsFalse(spec.Equals(!sourceSpec2));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var sourceSpec = new AdHocSpecification<string>(x => x.Length > 0);\n            var spec1 = !sourceSpec;\n            var spec2 = !sourceSpec;\n\n            Assert.IsInstanceOf<NotSpecification<string>>(spec1);\n            Assert.IsInstanceOf<NotSpecification<string>>(spec2);\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/Operators/OrSpecificationTests.cs",
    "content": "﻿using System;\nusing LinqSpecs.Operators;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class OrSpecificationTests\n    {\n        [Test]\n        public void Constructor_should_throw_exception_when_argument_is_null()\n        {\n            var spec = new AdHocSpecification<string>(x => x.Length == 1);\n\n            Assert.Throws<ArgumentNullException>(() => new OrSpecification<string>(spec, null!));\n            Assert.Throws<ArgumentNullException>(() => new OrSpecification<string>(null!, spec));\n        }\n\n        [Test]\n        public void Or_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n            var endsWithN = new AdHocSpecification<string>(n => n.EndsWith(\"n\"));\n\n            var result = new SampleRepository()\n                .Find(new OrSpecification<string>(startWithJ, endsWithN));\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.Contains(result, \"Julian\");\n            CollectionAssert.DoesNotContain(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Equals_returns_true_when_both_sides_are_equals()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var spec = s1 | s2;\n\n            Assert.IsInstanceOf<OrSpecification<string>>(spec);\n            Assert.IsTrue(spec.Equals(spec));\n            Assert.IsTrue(spec.Equals(s1 | s2));\n            Assert.IsTrue(spec.Equals(s1 || s2)); // | or || both operators behave as ||\n        }\n\n        [Test]\n        public void Equals_returns_false_when_both_sides_are_not_equals()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var s3 = new AdHocSpecification<string>(x => x.Length > 3);\n            var spec = s1 | s2;\n\n            Assert.IsInstanceOf<OrSpecification<string>>(spec);\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(s1));\n            Assert.IsFalse(spec.Equals(s2));\n            Assert.IsFalse(spec.Equals(s2 | s1)); // OrElse is not commutable\n            Assert.IsFalse(spec.Equals(s1 | s3));\n            Assert.IsFalse(spec.Equals(s3 | s2));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var s1 = new AdHocSpecification<string>(x => x.Length > 1);\n            var s2 = new AdHocSpecification<string>(x => x.Length > 2);\n            var s3 = new AdHocSpecification<string>(x => x.Length > 3);\n            var spec1 = s1 | s2 | s3;\n            var spec2 = s1 | s2 | s3;\n\n            Assert.IsInstanceOf<OrSpecification<string>>(spec1);\n            Assert.IsInstanceOf<OrSpecification<string>>(spec2);\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/SpecificationTests.cs",
    "content": "﻿using System;\nusing System.Linq.Expressions;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class SpecificationTests\n    {\n        [Test]\n        public void Implicit_operator_should_convert_specification_to_expression()\n        {\n            Specification<string> spec = new AdHocSpecification<string>(s => s.Length == 2);\n            Expression<Func<string, bool>> expr = spec;\n\n            Assert.IsTrue(expr.Compile().Invoke(\"ab\"));\n            Assert.IsFalse(expr.Compile().Invoke(\"abcd\"));\n        }\n\n        [Test]\n        public void Implicit_operator_should_throw_exception_when_argument_is_null()\n        {\n            Specification<string> spec = null!;\n\n            Assert.Throws<ArgumentNullException>(() =>\n            {\n                Expression<Func<string, bool>> expr = spec;\n            });\n        }\n\n        [Test]\n        public void And_operator_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n            var endsWithE = new AdHocSpecification<string>(n => n.EndsWith(\"e\"));\n\n            var result = new SampleRepository().Find(startWithJ & endsWithE);\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.DoesNotContain(result, \"Julian\");\n            CollectionAssert.DoesNotContain(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Or_operator_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n            var endsWithN = new AdHocSpecification<string>(n => n.EndsWith(\"n\"));\n\n            var result = new SampleRepository().Find(startWithJ | endsWithN);\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.Contains(result, \"Julian\");\n            CollectionAssert.DoesNotContain(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Negate_operator_should_work()\n        {\n            var startWithJ = new AdHocSpecification<string>(n => n.StartsWith(\"J\"));\n\n            var result = new SampleRepository().Find(!startWithJ);\n\n            CollectionAssert.DoesNotContain(result, \"Jose\");\n            CollectionAssert.DoesNotContain(result, \"Julian\");\n            CollectionAssert.Contains(result, \"Manuel\");\n        }\n\n        [Test]\n        public void AndAlso_operator_is_equivalent_to_And_operator()\n        {\n            var spec1 = new AdHocSpecification<string>(n => n.Length > 2);\n            var spec2 = new AdHocSpecification<string>(n => n.Length < 5);\n\n            Assert.AreEqual(spec1 & spec2, spec1 && spec2);\n        }\n\n        [Test]\n        public void OrElse_operator_is_equivalent_to_Or_operator()\n        {\n            var spec1 = new AdHocSpecification<string>(n => n.Length < 2);\n            var spec2 = new AdHocSpecification<string>(n => n.Length > 5);\n\n            Assert.AreEqual(spec1 | spec2, spec1 || spec2);\n        }\n\n        [Test]\n        public void Combination_of_boolean_operators_should_work()\n        {\n            var startWithM = new AdHocSpecification<string>(n => n.StartsWith(\"M\"));\n            var endsWithN = new AdHocSpecification<string>(n => n.EndsWith(\"n\"));\n            var containsU = new AdHocSpecification<string>(n => n.Contains(\"u\"));\n\n            var result = new SampleRepository().Find(startWithM | (!endsWithN & !containsU));\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.DoesNotContain(result, \"Julian\");\n            CollectionAssert.Contains(result, \"Manuel\");\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.UnitTests/TrueSpecificationTests.cs",
    "content": "﻿using System;\nusing NUnit.Framework;\n\nnamespace LinqSpecs.Tests\n{\n    [TestFixture]\n    public class TrueSpecificationTests\n    {\n        [Test]\n        public void Should_work()\n        {\n            var spec = new TrueSpecification<string>();\n\n            var result = new SampleRepository().Find(spec);\n\n            CollectionAssert.Contains(result, \"Jose\");\n            CollectionAssert.Contains(result, \"Julian\");\n            CollectionAssert.Contains(result, \"Manuel\");\n        }\n\n        [Test]\n        public void Equals_returns_true_when_both_sides_are_equals()\n        {\n            var spec1 = new TrueSpecification<string>();\n            var spec2 = new TrueSpecification<string>();\n\n            Assert.IsTrue(spec1.Equals(spec2));\n        }\n\n        [Test]\n        public void Equals_returns_false_when_both_sides_are_not_equals()\n        {\n            var spec = new TrueSpecification<string>();\n\n            Assert.IsFalse(spec.Equals(10));\n            Assert.IsFalse(spec.Equals(new AdHocSpecification<string>(x => true)));\n            Assert.IsFalse(spec.Equals(new TrueSpecification<object>()));\n            Assert.IsFalse(spec.Equals(null!));\n        }\n\n        [Test]\n        public void GetHashCode_retuns_same_value_for_equal_specifications()\n        {\n            var spec1 = new TrueSpecification<string>();\n            var spec2 = new TrueSpecification<string>();\n\n            Assert.AreEqual(spec1.GetHashCode(), spec2.GetHashCode());\n        }\n    }\n}\n"
  },
  {
    "path": "LinqSpecs.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30128.74\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"LinqSpecs\", \"LinqSpecs\\LinqSpecs.csproj\", \"{D60875A3-0DE2-409D-86A0-F540224D054C}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"LinqSpecs.UnitTests\", \"LinqSpecs.UnitTests\\LinqSpecs.UnitTests.csproj\", \"{0FBC6204-F18A-4D96-9C5E-39F925E7427C}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"LinqSpecs.DatabaseTests\", \"LinqSpecs.DatabaseTests\\LinqSpecs.DatabaseTests.csproj\", \"{B269AA58-028E-4BA7-ADBC-3848A4018CAE}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{F7AD41A5-AE94-4151-9527-13EC4FFA18C8}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.editorconfig = .editorconfig\n\tEndProjectSection\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{D60875A3-0DE2-409D-86A0-F540224D054C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{D60875A3-0DE2-409D-86A0-F540224D054C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{D60875A3-0DE2-409D-86A0-F540224D054C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{D60875A3-0DE2-409D-86A0-F540224D054C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{0FBC6204-F18A-4D96-9C5E-39F925E7427C}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{B269AA58-028E-4BA7-ADBC-3848A4018CAE}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {2B57D1D0-447D-4C30-A33C-51CA138160CE}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "README.md",
    "content": "![](https://github.com/navozenko/LinqSpecs/blob/master/logo.png)\n\nLinqSpecs 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).\n\nAlmost 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.\n\n[![NuGet](https://img.shields.io/nuget/v/LinqSpecs.svg)](https://nuget.org/packages/LinqSpecs)\n[![Nuget](https://img.shields.io/nuget/dt/LinqSpecs.svg)](https://nuget.org/packages/LinqSpecs) \n\n### Defining simple specifications\n\nIn order to define our first specification named \"CustomerFromCountrySpec\" we need to inherit from Specification\\<T\\>:\n\n```csharp\npublic abstract class Specification<T>\n{\n    public abstract Expression<Func<T, bool>> ToExpression();\n}\n```\n\nSo this is our implementation:\n\n```csharp\nusing LinqSpecs;\n\npublic enum Country { Argentina, France, Italia, ... }\n\npublic class CustomerFromCountrySpec : Specification<Customer>\n{\n    public Country Country { get; set; }\n\n    public CustomerFromCountrySpec(Country country)\n    {\n        Country = country;\n    }\n\n    public override Expression<Func<Customer, bool>> ToExpression()\n    { \n        return c => c.Country == Country;\n    }\n}\n```\n\nSimple as is, to use this class, your repository or DAO should implement these kind of methods:\n\n```csharp\npublic IEnumerable<T> Find(Specification<T> specification)\n{\n    return [a queryable source].Where(specification).ToList();\n}\n\npublic int Count(Specification<T> specification)\n{\n    return [a queryable source].Count(specification);\n}\n```\n\nThe usage is very simple:\n\n```csharp\nvar spec = new CustomerFromCountrySpec(Country.Argentina);\nvar customersFromArgentina = customerRepository.Find(spec);\n```\n\n### Alternative way to expose specifications\n\nAn alternative way of exposing specifications is with a static class:\n\n```csharp\npublic static class CustomerSpecs\n{\n    public static Specification<Customer> FromCountry(Country country) \n    { \n        return new CustomerFromCountrySpec(country);\n    }\n\n    public static Specification<Customer> EligibleForDiscount(decimal discount)\n    {\n        return new AdHocSpecification<Customer>(\n            c => c.IsPreferred && !c.HasDebt &&\n                 c.LastPurchaseDate > DateTime.Today.AddDays(-30));\n    }\n}\n```\n\nUsage:\n\n```csharp\ncustomerRepository.Find(\n    CustomerSpecs.FromCountry(argentina) &&\n    CustomerSpecs.EligibleForDiscount(3223));\n```\n\n### Logical operations AND, OR, NOT\n\nOne of the most interesting features of LinqSpecs is that you can combine known specifications with \"!\", \"&&\" and \"||\" operators. For example:\n\n```csharp\nvar spec1 = new CustomerFromCountrySpec(Country.Argentina);\nvar spec2 = new CustomerPreferredSpec();\nvar result = customerRepository.Find(spec1 && !spec2);\n```\n\nThis code returns all customers from Argentina that are not preferred. The & operator work as an && (AndAlso) operator. The same for | and ||.\n\n### Comparing\n\nThe result of and'ing, or'ing and negating specifications implements equality members. That's said:\n\n```csharp\n// This returns true\n(spec1 && spec2).Equals(spec1 && spec2);\n\n// This returns true\n(spec1 && (spec2 || !spec3)).Equals(spec1 && (spec2 || !spec3));\n\n// This returns false, because AndAlso and OrElse are not commutable operations\n(spec1 && spec2).Equals(spec2 && spec1);\n```\n\nThis is an useful feature when you are writing Asserts in your unit tests.\n\n### AdHocSpecification\n\nThe 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.\n\n```csharp\nvar spec = new AdHocSpecification<Customer>(c => c.IsPreferred && !c.HasDebt);\n```\n\n### TrueSpecification and FalseSpecification\n\nThe TrueSpecification is satisfied by any object. The FalseSpecification is not satisfied by any object.\n\n```csharp\n// This returns all customers\ncustomerRepository.Find(new TrueSpecification<Customer>());\n\n// This returns nothing\ncustomerRepository.Find(new FalseSpecification<Customer>());\n```\n\nThese 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:\n\n```csharp\nSpecification<Customer> spec = new FalseSpecification<Customer>();\nforeach (var country in countries)\n    spec |= new CustomerFromCountrySpec(country);\nreturn spec;\n```\n\n### In-memory queries\n\nAlthough 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:\n\n```csharp\nIEnumerable<Customer> customers = ...\nvar spec = new CustomerFromCountrySpec(Country.Argentina);\nvar result = customers.Where(spec.ToExpression().Compile());\n```\n\nCompiling 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.\n\n# Supported platforms\n\n- .NET Standard 2.0+\n- .NET Framework 4.0+\n- .NET Core 2.0+\n\n# License\n\nLinqSpecs is open-sourced software licensed under the [Microsoft Public License (MS-PL)](https://opensource.org/licenses/MS-PL).\n\n# Contributors\n\nLinqSpecs was created by [José F. Romaniello](https://github.com/jfromaniello) and [Sergey Navozenko](https://github.com/navozenko).\n"
  },
  {
    "path": "license.txt",
    "content": "Microsoft Public License (Ms-PL)\n\nThis 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.\n\n1. Definitions\n\nThe terms \"reproduce,\" \"reproduction,\" \"derivative works,\" and \"distribution\" have the same meaning here as under U.S. copyright law.\n\nA \"contribution\" is the original software, or any additions or changes to the software.\n\nA \"contributor\" is any person that distributes its contribution under this license.\n\n\"Licensed patents\" are a contributor's patent claims that read directly on its contribution.\n\n2. Grant of Rights\n\n(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.\n\n(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.\n\n3. Conditions and Limitations\n\n(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.\n\n(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.\n\n(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.\n\n(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.\n\n(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.\n"
  }
]