├── .gitignore ├── README.md ├── docs ├── CQRS-Vertical-Slices.md ├── Cross-Cutting-Concerns.md ├── DDD.md ├── Expressions.md ├── Extensions.md ├── Getting-started.md ├── LINQ.md ├── Reflection.md ├── index.rst └── text.txt ├── license.txt └── src ├── Force.Tests ├── Cqrs │ ├── FilterQueryTests.cs │ ├── HandlerTests.cs │ └── IntToStringHandler.cs ├── Ddd │ ├── DomainEvent.cs │ ├── DomainEventDispatcher.cs │ ├── DomainEventsStoreTests.cs │ ├── HasIdTests.cs │ ├── IdTests.cs │ ├── ResultTests.cs │ ├── SpecTests.cs │ ├── UnitOfWork.cs │ ├── UnitOfWorkTests.cs │ ├── ValueObjectTests.cs │ └── ValueObjects │ │ ├── StringValueObject.cs │ │ └── TestValueObject.cs ├── Expressions │ ├── ExpressionCompilerBenchmark.cs │ ├── ExpressionExtensionsTests.cs │ ├── FormatterTests.cs │ ├── OrderChecker.cs │ ├── PredicateBuilderTests.cs │ ├── QueryableTests.cs │ └── SorterTests.cs ├── ExpressionsTests.cs ├── Extensions │ ├── EnumExtensionsTests.cs │ ├── FunctionalExtensionsTests.cs │ └── ValidationResultExtensionTests.cs ├── Force.Tests.csproj ├── Infrastructure │ ├── Context │ │ ├── Category.cs │ │ ├── DbContextFixture.cs │ │ ├── DbContextFixtureTestsBase.cs │ │ ├── Product.cs │ │ ├── ProductFilter.cs │ │ ├── ProductListItem.cs │ │ └── TestsDbContext.cs │ ├── PagedProductFilter.cs │ ├── TestCase.cs │ └── TestCaseBuilder.cs ├── Linq │ ├── FilterConventionsTests │ │ ├── EnumerableConventionTests.cs │ │ └── FilterConventionsTests.cs │ ├── PagedEnumerableTests.cs │ ├── PagingTests.cs │ ├── QueryableExtensionsTests.cs │ ├── SearchByTests.cs │ └── SorterTests.cs ├── QueryTests.cs ├── Reflection │ └── TypeTests.cs ├── SpecBuilderTests.cs ├── SpecTests.cs ├── TypeTestObject.cs └── TypeTests.cs ├── Force.sln └── Force ├── Ccc ├── IHasUserFrendlyMessage.cs ├── IPermissionFilter.cs ├── IUnitOfWork.cs ├── IUnitOfWorkTransaction.cs ├── IValidator.cs ├── Result.cs └── UnitOfWorkBase.cs ├── Cqrs ├── FilterQuery.cs ├── FilterQueryAsync.cs ├── FilterQueryBase.cs ├── ICommand.cs ├── ICommandHandler.cs ├── IHandler.cs ├── IQuery.cs ├── IQueryHandler.cs ├── PagedFilterQuery.cs ├── PagedFilterQueryAsync.cs └── PagedFilterQueryBase.cs ├── Ddd ├── DomainEvents │ ├── DomainEventStore.cs │ ├── IDomainEvent.cs │ └── IHasDomainEvents.cs ├── Formatter.cs ├── HasIdBase.cs ├── HasNameBase.cs ├── IHasId.cs ├── Id.cs ├── Spec.cs ├── SpecBuilder.cs ├── Strings.cs └── ValueObject.cs ├── Expressions ├── CompiledExpressions.cs ├── ExpressionExtensions.cs └── PredicateBuilder.cs ├── Extensions ├── EnumExtensions.cs └── FunctionalExtensions.cs ├── Force.csproj ├── Linq ├── ComposeKind.cs ├── Conventions │ ├── EnumerableConvention.cs │ ├── FilterConventionBase.cs │ ├── FilterConventions.cs │ ├── IFilterConvention.cs │ └── StringConvention.cs ├── IFilter.cs ├── ISorter.cs ├── Pagination │ ├── IPaging.cs │ ├── PagedEnumerable.cs │ └── Paging.cs ├── QueryableExtentions.cs ├── SearchByAttribute.cs └── Sorter.cs └── Reflection ├── Type.cs └── TypeExtensions.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ################### 2 | # compiled source # 3 | ################### 4 | *.com 5 | *.class 6 | *.dll 7 | *.exe 8 | *.pdb 9 | *.dll.config 10 | *.cache 11 | *.suo 12 | # Include dlls if they’re in the NuGet packages directory 13 | !/packages/*/lib/*.dll 14 | !/packages/*/lib/*/*.dll 15 | # Include dlls if they're in the CommonReferences directory 16 | !*CommonReferences/*.dll 17 | #################### 18 | # VS Upgrade stuff # 19 | #################### 20 | UpgradeLog.XML 21 | _UpgradeReport_Files/ 22 | ############### 23 | # Directories # 24 | ############### 25 | bin/ 26 | obj/ 27 | TestResults/ 28 | /.idea/ 29 | **/.idea/ 30 | **/.idea/* 31 | /.idea/* 32 | ################### 33 | # Web publish log # 34 | ################### 35 | *.Publish.xml 36 | 37 | ############# 38 | # Resharper # 39 | ############# 40 | /_ReSharper.* 41 | *.ReSharper.* 42 | ############ 43 | # Packages # 44 | ############ 45 | # it’s better to unpack these files and commit the raw source 46 | # git has its own built in compression methods 47 | *.7z 48 | *.dmg 49 | *.gz 50 | *.iso 51 | *.jar 52 | *.rar 53 | *.tar 54 | *.zip 55 | ###################### 56 | # Logs and databases # 57 | ###################### 58 | *.log 59 | *.sqlite 60 | # OS generated files # 61 | ###################### 62 | .DS_Store? 63 | ehthumbs.db 64 | Icon? 65 | Thumbs.db 66 | [Bb]in 67 | [Oo]bj 68 | [Tt]est[Rr]esults 69 | *.suo 70 | *.user 71 | *.[Cc]ache 72 | *[Rr]esharper* 73 | packages 74 | NuGet.exe 75 | _[Ss]cripts 76 | *.exe 77 | *.dll 78 | *.nupkg 79 | *.ncrunchsolution 80 | *.dot[Cc]over 81 | ###################### 82 | node_modules 83 | *.min.js 84 | *.min.css 85 | project.lock.json 86 | *.DotSettings.user 87 | *.lock.json 88 | /src/.vs 89 | .vs 90 | 91 | /src/.idea 92 | /DemoApp.Web/wwwroot/semantic-ui 93 | /DemoApp.Web/wwwroot/bootstrap.css 94 | /DemoApp.Web/wwwroot/index.html 95 | /.idea 96 | .DS_Store 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Force 2 | 3 | General-purpose library for rapid .NET applications development. 4 | Built around CQRS and DDD approaches. 5 | 6 | Install stable version into your project via Nuget: 7 | 8 | Install-Package Force 9 | 10 | And when you start, just keep 11 | 12 | using Force; // :-) -------------------------------------------------------------------------------- /docs/CQRS-Vertical-Slices.md: -------------------------------------------------------------------------------- 1 | # CQRS, Vertical Slices 2 | Vertical Slices architecture is built around distinct requests, encapsulating and grouping all concerns from front-end to back. You take a normal "n-tier" or hexagonal/whatever architecture and remove the gates and barriers across those layers, and couple along the axis of change. The style is used and popularized by: 3 | - Jimmy Boggard: [Vertical slice architecture](https://jimmybogard.com/vertical-slice-architecture/), [NDC talk](https://www.youtube.com/watch?v=SUiWfhAhgQw), [NDC talk](https://www.youtube.com/watch?v=T6nglsEDaqA) 4 | - Steven van Deursen: [Meanwhile on the command side of my architecture](https://blogs.cuttingedge.it/steven/posts/2011/meanwhile-on-the-command-side-of-my-architecture/), [Meanwhile on the query side of my architecture](https://blogs.cuttingedge.it/steven/posts/2011/meanwhile-on-the-query-side-of-my-architecture/) 5 | - Maxim Arshinov: [Instant Design](https://habr.com/ru/company/jugru/blog/447308/) (In Russian) 6 | 7 | ## IHandler 8 | Todo: add docs 9 | 10 | ## ICommand, ICommandHandler 11 | Todo: add docs 12 | 13 | ## IQuery, IQueryHandler 14 | Todo: add docs 15 | 16 | ## FilterQuery, PagedFilterQuery 17 | Todo: add docs -------------------------------------------------------------------------------- /docs/Cross-Cutting-Concerns.md: -------------------------------------------------------------------------------- 1 | # Cross-cutting concerns 2 | 3 | ## IPermissionFilter 4 | https://habr.com/ru/post/414897/ 5 | 6 | ## UnitOfWork 7 | For domain events and notifications 8 | 9 | ## Error Handling 10 | 11 | ### IHasUserFrendlyMessage 12 | 13 | ### Result 14 | https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/results 15 | 16 | ## IValidator 17 | IValidator + IHadnler + [Result] = Love -------------------------------------------------------------------------------- /docs/DDD.md: -------------------------------------------------------------------------------- 1 | # DDD 2 | Domain-driven design (DDD) is the concept that the structure and language of your code (class names, class methods, class variables) should match the business domain. For example, if your software processes loan applications, it might have classes such as LoanApplication and Customer, and methods such as AcceptOffer and Withdraw. The term was coined by Eric Evans in his book of the same title. 3 | 4 | ## Entities 5 | There is a category of objects which seem to have an identity, which remains the same throughout the states of the software. Such objects are called Entities. 6 | 7 | ### Id 8 | TODO: Doc 9 | 10 | ### IHasId 11 | IHasId, IHasId 12 | Generic and non-generic versions of the IHasId attribute are used both for business objects and their projections (DTO’s). These interfaces are essential for some handy extension methods described in the “meta-programming” section of the documentation. 13 | 14 | ### HasIdBase: IHasId, HasIdBase: IHasId 15 | The only reason for the existence of these classes is to avoid code duplication in your codebase because typically there are lots of IDs in every business app. You might also want to declare more advanced classes for your entities. For instance, making your Id property setter non-public maybe a good thing for your design in terms of reliability and maintainability. The good news is that you can write these classes by yourself. Force is not a framework, so we try to keep it as simple and lightweight as possible. 16 | 17 | ### HasNameBase: HasIdBase 18 | This is just a more specific base class derived from the HasIdBase. Literally every single business app has at least one entity with a name. That’s why this base class is included. 19 | 20 | ## ValueObjects 21 | There are cases when we need to contain some attributes of a domain element. We are not interested in which object it is, but what attributes it has. An object that is used to describe certain aspects of a domain, and which does not have an identity, is named Value Object. 22 | The ValueObject implementation is inspired by Vladimir Khorikov’s article “Value Object: a better implementation” 23 | 24 | ## Spec 25 | Specification (Spec)is a pattern that allows us to encapsulate some piece of domain knowledge into a single unit - specification - and reuse it in different parts of the codebase. C# implementation of the pattern benefits from using the Expression Trees so that it can be applied to both IQueryable and IEnumerable. Compiling an expression tree is a relatively slow operation so it’s cached internally so that the Compile method will run only once per specification instance. That’s why it’s recommended to define specifications as static read-only variables. 26 | 27 | Spec implementation supports &,&&, |,||, and ! operators and also provides an extension method From which helps to build new specifications composing the existing ones within a single aggregate. 28 | 29 | ## SpecBuilder 30 | SpecBuilder is a powerful tool for creating specifications by conventions. See the FilterConventions documentation for more details. 31 | 32 | ## Domain Events 33 | The need for domain events comes from a desire to inject services into domain models. What we really want is to create an encapsulated domain model, but decouple ourselves from potential side effects and isolate those explicitly. The domain events implementation is based on Jimmy Boggard’s article “A better domain events pattern” and slightly improved. 34 | 35 | ## DomainEventStore -------------------------------------------------------------------------------- /docs/Expressions.md: -------------------------------------------------------------------------------- 1 | # Expressions 2 | Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y. 3 | 4 | You can compile and run code represented by expression trees. This enables dynamic modification of executable code, the execution of LINQ queries in various databases, and the creation of dynamic queries. 5 | 6 | https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/ 7 | 8 | ## PredicateBuilder 9 | https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ 10 | 11 | ## ExpressionExtensions 12 | AsFunc 13 | From 14 | https://habr.com/ru/post/313394/ 15 | -------------------------------------------------------------------------------- /docs/Extensions.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hightechgroup/force/ec3f31fb42ebecead88f52eafd9789cd8a66bf06/docs/Extensions.md -------------------------------------------------------------------------------- /docs/Getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started Guide 2 | 3 | ## Why use Force? 4 | You might benefit from using Force if: 5 | - Strive to follow DDD principles and use DDD patterns in your application. Force ships with built-in support for Entities, Value Objects, Specifications, and Domain Events. 6 | - You are a fan of the Vertical Slices / CQRS architecture and/or concern-separation matters to you. Force provides you ICommand / ICommandHadler and IQuery / IQueryHandler interfaces and some extension-methods that help you to avoid writing the same boilerplate code again and again. 7 | - Need reliable tooling for some fancy metaprogramming stuff. 8 | 9 | ## How do I use Force? 10 | It’s simple. Just get official builds from NuGet or run the following command in the Package Manager Console: PM> Install-Package Force and may the force be with you. No additional configuration required. -------------------------------------------------------------------------------- /docs/LINQ.md: -------------------------------------------------------------------------------- 1 | # LINQ 2 | This is a set of classes and extensions for IQueryable that help to write significantly less boring code related to data filtering, sorting, and pagination. 3 | 4 | TODO: example. FilterSortAndPaginate. 5 | 6 | ## Filter Conventions 7 | 8 | ## Pagination 9 | 10 | ## IFilter 11 | 12 | ## ISorter 13 | 14 | ## QueryableExtensions 15 | 16 | ## SearchByAttribute 17 | 18 | ## Sorter 19 | 20 | -------------------------------------------------------------------------------- /docs/Reflection.md: -------------------------------------------------------------------------------- 1 | # Reflection 2 | 3 | ## Type 4 | 5 | ## TypeExtensions -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Force 2 | ========== 3 | 4 | Force is a light-weight library that helps to build .NET business apps faster and writing less code. It’s open-source and supports .net core 3.1. The code has no platform-specific dependencies so feel free to compile it using .NET Framework if you need it. 5 | 6 | New to Force? Check out the :doc:`Getting-started` page first. 7 | 8 | .. _user-docs: 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :caption: User Documentation 13 | 14 | Getting-started 15 | 16 | .. _feature-docs: 17 | 18 | .. toctree:: 19 | :maxdepth: 2 20 | :caption: Features 21 | 22 | CQRS-Vertical-Slices 23 | Cross-Cutting-Concerns 24 | DDD 25 | Expressions 26 | Extensions 27 | LINQ 28 | Reflection -------------------------------------------------------------------------------- /docs/text.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | # CQRS, Vertical Slices 4 | Command, Query, Handlers 5 | 6 | https://blogs.cuttingedge.it/steven/posts/2011/meanwhile-on-the-command-side-of-my-architecture/ 7 | https://blogs.cuttingedge.it/steven/posts/2011/meanwhile-on-the-query-side-of-my-architecture/ 8 | 9 | # DDD 10 | Entities, Value Objects 11 | https://enterprisecraftsmanship.com/posts/entity-base-class/ 12 | https://enterprisecraftsmanship.com/posts/value-object-better-implementation/ 13 | 14 | Result 15 | https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/results 16 | https://fsharpforfunandprofit.com/rop/ 17 | 18 | Spec 19 | https://github.com/navozenko/LinqSpecs 20 | 21 | Unit of work 22 | https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/ 23 | 24 | 25 | # Expressions 26 | https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/ 27 | 28 | # Extensions 29 | 30 | # LINQ 31 | Filtering By Conventions 32 | https://github.com/StefH/System.Linq.Dynamic.Core 33 | Pagination Support 34 | 35 | # Reflection 36 | Wrappers... -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /src/Force.Tests/Cqrs/FilterQueryTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Linq; 3 | using Force.Reflection; 4 | using Force.Tests.Infrastructure; 5 | using Force.Tests.Infrastructure.Context; 6 | using Microsoft.EntityFrameworkCore.Internal; 7 | using Xunit; 8 | 9 | namespace Force.Tests.Cqrs 10 | { 11 | public class FilterQueryTests: DbContextFixtureTestsBase 12 | { 13 | public FilterQueryTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 14 | { 15 | } 16 | 17 | [Fact] 18 | public void Search() 19 | { 20 | var productFilter = new PagedProductFilter() 21 | { 22 | Search = DbContextFixture.FirstProductName 23 | }; 24 | 25 | var results = DbContext 26 | .Products 27 | .Filter(productFilter) 28 | .ToList(); 29 | 30 | Assert.Collection(results, 31 | x => x.Name.StartsWith(DbContextFixture.FirstProductName)); 32 | } 33 | 34 | [Fact] 35 | public void Sort_Asc() 36 | { 37 | var productFilter = new PagedProductFilter() 38 | { 39 | Order = "Name", 40 | Asc = false 41 | }; 42 | 43 | var sortedExpected = DbContext 44 | .Products 45 | .OrderByDescending(x => x.Name) 46 | .Select(x => x.Name) 47 | .ToArray(); 48 | 49 | var sorted = productFilter 50 | .Sort(DbContext.Products) 51 | .Select(x => x.Name) 52 | .ToArray(); 53 | 54 | Assert.All(sorted, 55 | x => Assert.Equal(sorted.IndexOf(x), sortedExpected.IndexOf(x))); 56 | } 57 | 58 | [Fact] 59 | public void Filter() 60 | { 61 | var pp = Type.PublicProperties; 62 | 63 | var productFilter = new PagedProductFilter() 64 | { 65 | Name = DbContextFixture.FirstProductName 66 | }; 67 | 68 | var results = productFilter 69 | .Filter(DbContext.Products) 70 | .ToList(); 71 | 72 | Assert.Collection(results, 73 | x => Assert.Equal(DbContextFixture.FirstProductName, x.Name)); 74 | } 75 | 76 | 77 | } 78 | } -------------------------------------------------------------------------------- /src/Force.Tests/Cqrs/HandlerTests.cs: -------------------------------------------------------------------------------- 1 | using Force.Cqrs; 2 | using Force.Extensions; 3 | using Xunit; 4 | 5 | namespace Force.Tests.Cqrs 6 | { 7 | public class HandlerTests 8 | { 9 | [Fact] 10 | public void PipeTo() 11 | { 12 | var handler = new IntToStringHandler(); 13 | var res = handler 14 | .Handle(1) 15 | .PipeTo(x => x + "!"); 16 | 17 | Assert.Equal("1!", res); 18 | } 19 | 20 | [Fact] 21 | public void ToFunc() 22 | { 23 | var handler = new IntToStringHandler(); 24 | var res = handler 25 | .Handle(1) 26 | .PipeTo(x => x + "!"); 27 | 28 | Assert.Equal("1!", res); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/Force.Tests/Cqrs/IntToStringHandler.cs: -------------------------------------------------------------------------------- 1 | using Force.Cqrs; 2 | 3 | namespace Force.Tests.Cqrs 4 | { 5 | class IntToStringHandler: IHandler 6 | { 7 | public string Handle(int input) => input.ToString(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/DomainEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Force.Ddd.DomainEvents; 3 | 4 | namespace Force.Tests.Ddd 5 | { 6 | public class DomainEvent : IDomainEvent 7 | { 8 | public DateTime Happened { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/DomainEventDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Force.Cqrs; 3 | using Force.Ddd.DomainEvents; 4 | 5 | namespace Force.Tests.Ddd 6 | { 7 | public class DomainEventDispatcher: IHandler> 8 | { 9 | public void Handle(IEnumerable input) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/DomainEventsStoreTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using Force.Ddd.DomainEvents; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Ddd 7 | { 8 | public class DomainEventsStoreTests 9 | { 10 | static readonly DomainEvent De = new DomainEvent(); 11 | 12 | [Fact] 13 | public void Raise() 14 | { 15 | var des = new DomainEventStore(); 16 | des.Raise(De); 17 | 18 | AssertEnumerator(des.GetEnumerator()); 19 | AssertEnumerator(((IEnumerable)des).GetEnumerator()); 20 | } 21 | 22 | private void AssertEnumerator(IEnumerator enumerator) 23 | { 24 | Assert.True(enumerator.MoveNext()); 25 | Assert.Equal(De, enumerator.Current); 26 | Assert.False(enumerator.MoveNext()); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/HasIdTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Ddd; 3 | using Force.Tests.Infrastructure.Context; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Ddd 7 | { 8 | public class HasIdTests: IClassFixture 9 | { 10 | private readonly DbContextFixture _dbContextFixture; 11 | 12 | public HasIdTests(DbContextFixture dbContextFixture) 13 | { 14 | _dbContextFixture = dbContextFixture; 15 | } 16 | 17 | [Fact] 18 | public void IsNew() 19 | { 20 | var pr = _dbContextFixture.DbContext.Products.First(); 21 | Assert.False(pr.IsNew()); 22 | 23 | // ReSharper disable once RedundantCast 24 | int obj = (int)((IHasId) pr).Id; 25 | Assert.Equal(1, obj); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/IdTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Force.Ddd; 4 | using Force.Linq; 5 | using Force.Tests.Infrastructure.Context; 6 | using Xunit; 7 | 8 | namespace Force.Tests.Ddd 9 | { 10 | public class IdTests: DbContextFixtureTestsBase 11 | { 12 | public IdTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void New_LoaderIsNull_ThrowsArgumentNullException() 18 | { 19 | Assert.Throws(() => 20 | { 21 | var id = new Id(1, null); 22 | }); 23 | 24 | Assert.Throws(() => 25 | { 26 | var id = new Id(1, null); 27 | }); 28 | } 29 | 30 | [Fact] 31 | public void New_ValueIsZero_ThrowsArgumentException() 32 | { 33 | Assert.Throws(() => 34 | { 35 | var id = new Id(0, x => null); 36 | }); 37 | } 38 | 39 | [Fact] 40 | public void New_IsNew_ThrowsArgumentException() 41 | { 42 | Assert.Throws(() => 43 | { 44 | var id = new Id(new Product(new Category("Name"), "Name")); 45 | }); 46 | } 47 | 48 | [Fact] 49 | public void New_ExistingEntity_CreatesNewInstance() 50 | { 51 | var id = new Id(DbContext.Products.First()); 52 | } 53 | 54 | [Fact] 55 | public void New_CorrectValueAndLoader_CreatesNewInstance() 56 | { 57 | var id = new Id(1, x => null); 58 | } 59 | 60 | 61 | [Fact] 62 | public void TryParse_ValueIsZeroReturnsFalseIdIsNull() 63 | { 64 | var res = Id.TryParse(0, x => null, out var id); 65 | 66 | Assert.False(res); 67 | Assert.Null(id); 68 | 69 | res = Id.TryParse(0, x => null, out var id2); 70 | Assert.False(res); 71 | Assert.Null(id); 72 | } 73 | 74 | 75 | [Fact] 76 | public void TryParse_ValueIsValid_EntityIsNotNull() 77 | { 78 | Id.TryParse(1, x => DbContext.Products.FirstOrDefaultById(x), out var id); 79 | Assert.NotNull(id.Entity); 80 | } 81 | 82 | [Fact] 83 | public void Implicit_KeyValue() 84 | { 85 | Id.TryParse(1, x => null, out var id); 86 | int intId = id; 87 | Assert.Equal(1, intId); 88 | 89 | Id.TryParse(1, x => null, out var id2); 90 | intId = id2; 91 | Assert.Equal(1, intId); 92 | } 93 | 94 | [Fact] 95 | public void Implicit_Entity() 96 | { 97 | var id = new Id(DbContext.Products.First()); 98 | Product entity = id; 99 | id = entity; 100 | Assert.Equal(entity, id.Entity); 101 | 102 | var id2 = new Id(DbContext.Products.First()); 103 | Product entity2 = id2; 104 | id2 = entity2; 105 | Assert.Equal(entity2, id2.Entity); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/ResultTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Force.Ccc; 3 | using Force.Ddd; 4 | using Force.Reflection; 5 | using Xunit; 6 | 7 | namespace Force.Tests.Ddd 8 | { 9 | public class ResultTests 10 | { 11 | [Fact] 12 | public void Select() 13 | { 14 | var r = new Result("false"); 15 | r = r.Select(x => x + 5); 16 | 17 | Assert.True(r.IsFaulted); 18 | } 19 | 20 | [Fact] 21 | public void SelectMany() 22 | { 23 | var r = new Result(1); 24 | r = r.SelectMany( 25 | x => new Result(5), 26 | (i, i1) => i + i1); 27 | 28 | var res = r.Match(x => x, x => 0); 29 | Assert.Equal(6, res); 30 | } 31 | 32 | [Fact] 33 | public void Linq_SuccessAndFailure_ReturnsFailure() 34 | { 35 | var r1 = new Result(1); 36 | var r2 = new Result("false"); 37 | 38 | var a = from r11 in r1 select r1; 39 | var b = 40 | from r11 in r1 41 | from r22 in r2 42 | select r11 + r22; 43 | 44 | Assert.True(b.IsFaulted); 45 | } 46 | 47 | [Fact] 48 | public void New() 49 | { 50 | var r1 = new Result(1); 51 | var r2 = new Result("failure"); 52 | var r3 = new Result(new Exception("exception")); 53 | 54 | Assert.False(r1.IsFaulted); 55 | Assert.True(r2.IsFaulted); 56 | Assert.True(r3.IsFaulted); 57 | 58 | Assert.Equal("failure", r2.Match(x => "success", x => x)); 59 | Assert.Equal("exception", r3.Match(x => "success", x => x)); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/SpecTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Force.Ddd; 5 | using Force.Linq; 6 | using Force.Tests.Infrastructure.Context; 7 | using Microsoft.EntityFrameworkCore; 8 | using Xunit; 9 | 10 | namespace Force.Tests.Ddd 11 | { 12 | public class SpecTests: DbContextFixtureTestsBase 13 | { 14 | private static readonly Spec Spec1 15 | = new Spec(x => x.Name == DbContextFixture.FirstProductName); 16 | 17 | private static readonly Spec Spec2 18 | = new Spec(x => x.Id < DbContextFixture.LastProductId); 19 | 20 | public SpecTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 21 | { 22 | } 23 | 24 | private void TestComposed(Func, Spec,Spec> composeFunc, 25 | Action> assert) 26 | { 27 | var composed = composeFunc(Spec1, Spec2); 28 | var res = DbContext 29 | .Products 30 | .Where(composed) 31 | .ToList(); 32 | 33 | Assert.NotEmpty(res); 34 | assert(res); 35 | } 36 | 37 | [Fact] 38 | public void From() 39 | { 40 | var s = new Spec(x => x.Name == DbContextFixture.FirstCategoryName); 41 | var from = s.From(x => x.Category); 42 | 43 | var products = DbContext 44 | .Products 45 | .Include(x => x.Category) 46 | .Where(from) 47 | .ToList(); 48 | 49 | Assert.All(products, x => 50 | Assert.Equal(x.Category.Name, DbContextFixture.FirstCategoryName)); 51 | } 52 | 53 | [Fact] 54 | public void Not() 55 | { 56 | var s3 = !Spec1; 57 | 58 | var products = DbContext 59 | .Products 60 | .Where(s3) 61 | .ToList(); 62 | 63 | Assert.All(products, x => Assert.NotEqual(DbContextFixture.FirstCategoryName, x.Name)); 64 | } 65 | 66 | private void AssertOr(IList list) 67 | { 68 | Assert.All(list, x => 69 | { 70 | Assert.True(!x.Name.StartsWith(DbContextFixture.LastProductName)); 71 | }); 72 | } 73 | 74 | [Fact] 75 | public void Or() 76 | { 77 | TestComposed((s1, s2) => s1 | s2, AssertOr); 78 | } 79 | 80 | [Fact] 81 | public void DoubleOr() 82 | { 83 | TestComposed((s1, s2) => s1 || s2, AssertOr); 84 | } 85 | 86 | private void AssertAnd(IList list) 87 | { 88 | Assert.All(list, x => 89 | { 90 | Assert.True(x.Name.StartsWith(DbContextFixture.FirstProductName) 91 | || x.Name.StartsWith(DbContextFixture.LastProductName)); 92 | }); 93 | } 94 | 95 | [Fact] 96 | public void And() 97 | { 98 | TestComposed((s1, s2) => s1 & s2, AssertAnd); 99 | } 100 | 101 | [Fact] 102 | public void DoubleAnd() 103 | { 104 | TestComposed((s1, s2) => s1 && s2, AssertAnd); 105 | } 106 | 107 | [Fact] 108 | public void Satisfy() 109 | { 110 | var res = DbContext 111 | .Products 112 | .ToList(); 113 | 114 | var list =res 115 | .Where(x => Spec1.IsSatisfiedBy(x)) 116 | .ToList(); 117 | 118 | Assert.True(res.All(x => x.Id > 0)); 119 | } 120 | 121 | [Fact] 122 | public void BuildOr() 123 | { 124 | var spec = SpecBuilder.Build( 125 | new 126 | { 127 | Id = 1, Name = DbContextFixture.SecondProductName 128 | }, 129 | ComposeKind.Or); 130 | 131 | var res = DbContext 132 | .Products 133 | .Where(spec) 134 | .ToList(); 135 | 136 | Assert.All(res, x => Assert.True(x.Name == DbContextFixture.FirstProductName 137 | || x.Name == DbContextFixture.SecondProductName)); 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Transactions; 3 | using Force.Ccc; 4 | using Force.Cqrs; 5 | using Force.Ddd; 6 | using Force.Ddd.DomainEvents; 7 | 8 | namespace Force.Tests.Ddd 9 | { 10 | public class UnitOfWork: UnitOfWorkBase 11 | { 12 | public UnitOfWork(IHandler> domainEventDispatcher) : base(domainEventDispatcher) 13 | { 14 | } 15 | 16 | public override void Dispose() 17 | { 18 | throw new System.NotImplementedException(); 19 | } 20 | 21 | public override void Add(TEntity entity) 22 | { 23 | throw new System.NotImplementedException(); 24 | } 25 | 26 | public override void Remove(TEntity entity) 27 | { 28 | throw new System.NotImplementedException(); 29 | } 30 | 31 | public override TEntity Find(params object[] id) 32 | { 33 | throw new System.NotImplementedException(); 34 | } 35 | 36 | protected override void DoCommit() 37 | { 38 | } 39 | 40 | protected override IEnumerable GetDomainEvents() 41 | { 42 | return new IDomainEvent[] { }; 43 | } 44 | 45 | public override IUnitOfWorkTransaction BeginTransaction() 46 | { 47 | throw new System.NotImplementedException(); 48 | } 49 | 50 | public override void Update(TEntity entity) 51 | { 52 | throw new System.NotImplementedException(); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/UnitOfWorkTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Force.Tests.Ddd 4 | { 5 | public class UnitOfWorkTests 6 | { 7 | [Fact] 8 | public void Commit() 9 | { 10 | // TODO: add MOQ 11 | var uow = new UnitOfWork(new DomainEventDispatcher()); 12 | uow.Commit(); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/ValueObjectTests.cs: -------------------------------------------------------------------------------- 1 | using Force.Ddd; 2 | using Xunit; 3 | 4 | namespace Force.Tests.Ddd 5 | { 6 | public class ValueObjectTests 7 | { 8 | private static TestValueObject vo1 = new TestValueObject("vo1"); 9 | private static TestValueObject vo2 = new TestValueObject("vo2"); 10 | private static TestValueObject vo3 = new TestValueObject("vo1"); 11 | 12 | [Fact] 13 | public void Implicit() 14 | { 15 | string s = vo1; 16 | } 17 | 18 | [Fact] 19 | public void Equals_2() 20 | { 21 | var vo = new StringValueObject(); 22 | vo.Equals(vo); 23 | vo.Equals(null); 24 | var a = vo == vo; 25 | var b = vo != vo; 26 | 27 | vo.Equals(vo1); 28 | 29 | StringValueObject vo2 = null; 30 | a = vo2 == vo2; 31 | b = vo2 != vo2; 32 | a = vo == vo2; 33 | } 34 | 35 | [Fact] 36 | public void Equals_() 37 | { 38 | Assert.False(vo1.Equals(vo2)); 39 | Assert.False(vo1 == vo2); 40 | Assert.True(vo1 != vo2); 41 | 42 | Assert.False(vo2.Equals(vo3)); 43 | Assert.False(vo2 == vo3); 44 | Assert.True(vo2 != vo3); 45 | 46 | Assert.True(vo1.Equals(vo3)); 47 | Assert.True(vo1 == vo3); 48 | Assert.False(vo1 != vo3); 49 | } 50 | 51 | [Fact] 52 | public void GetHashCode_() 53 | { 54 | Assert.Equal(vo1.GetHashCode(), vo3.GetHashCode()); 55 | Assert.NotEqual(vo1.GetHashCode(), vo2.GetHashCode()); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/ValueObjects/StringValueObject.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Force.Ddd; 3 | 4 | namespace Force.Tests.Ddd 5 | { 6 | public class StringValueObject: ValueObject 7 | { 8 | public string Value { get; set; } 9 | 10 | protected override IEnumerable GetEqualityComponents() 11 | { 12 | yield return Value; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/Force.Tests/Ddd/ValueObjects/TestValueObject.cs: -------------------------------------------------------------------------------- 1 | using Force.Ddd; 2 | 3 | namespace Force.Tests.Ddd 4 | { 5 | public class TestValueObject: ValueObject 6 | { 7 | public TestValueObject(string value) : base(value) 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/ExpressionCompilerBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using BenchmarkDotNet.Attributes; 4 | using BenchmarkDotNet.Engines; 5 | using Force.Expressions; 6 | 7 | namespace Force.Tests.Expressions 8 | { 9 | //[SimpleJob(RunStrategy.Monitoring, launchCount: 10, warmupCount: 3, targetCount: 100)] 10 | [MemoryDiagnoser] 11 | public class ExpressionCompilerBenchmark 12 | { 13 | Expression> _expression = x => x.ToString().Length > 5; 14 | 15 | [Benchmark] 16 | public void Compile() 17 | { 18 | _expression.Compile(); 19 | } 20 | 21 | [Benchmark] 22 | public void AsFunc() 23 | { 24 | _expression.AsFunc(); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/ExpressionExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Force.Tests.Expressions 4 | { 5 | public class ExpressionExtensionsTests 6 | { 7 | [Fact] 8 | public void From() 9 | { 10 | 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/FormatterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using Force.Ddd; 5 | using Force.Tests.Infrastructure.Context; 6 | using Xunit; 7 | 8 | namespace Force.Tests.Expressions 9 | { 10 | public class FormatterTests: DbContextFixtureTestsBase 11 | { 12 | public FormatterTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void Select_Format() 18 | { 19 | var formatter = new Formatter(x => "Product Name is: " + x.Name); 20 | var res = DbContext 21 | .Products 22 | .Select(formatter) 23 | .ToList(); 24 | 25 | var cf = new Formatter(x => x.Id.ToString()); 26 | Expression> e = cf.From((Product p) => p.Category); 27 | 28 | Assert.True(res.All(x => x.Any() && x.StartsWith("Product Name is: "))); 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/OrderChecker.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Force.Tests.Infrastructure.Context; 3 | using static System.String; 4 | 5 | namespace Force.Tests.Expressions 6 | { 7 | internal static class OrderChecker 8 | { 9 | public static bool CheckOrder(List res, bool asc) 10 | { 11 | bool flag = true; 12 | string current = null; 13 | foreach (var p in res) 14 | { 15 | if (current == null) 16 | { 17 | current = p.Name; 18 | continue; 19 | } 20 | 21 | flag = asc 22 | ? CompareOrdinal(p.Name, current) >= 0 23 | : CompareOrdinal(p.Name, current) <= 0; 24 | 25 | if (!flag) 26 | { 27 | break; 28 | } 29 | } 30 | 31 | return flag; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/PredicateBuilderTests.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Tests.Expressions 2 | { 3 | public class PredicateBuilderTests 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/QueryableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Force.Linq; 5 | using Force.Tests.Infrastructure; 6 | using Force.Tests.Infrastructure.Context; 7 | using Xunit; 8 | 9 | namespace Force.Tests.Expressions 10 | { 11 | public class QueryableTests : DbContextFixtureTestsBase 12 | { 13 | public QueryableTests(DbContextFixture dbContextFixture) 14 | :base(dbContextFixture) 15 | { 16 | } 17 | 18 | [Fact] 19 | public void WhereIf() 20 | { 21 | DbContext 22 | .Products 23 | .WhereIf(true, x => true); 24 | } 25 | 26 | [Fact] 27 | public void OrderByAndOrderByDescending_PropertyDoesntExist_ThrowsArgumentException() 28 | { 29 | Assert.Throws(() => 30 | { 31 | DbContext 32 | .Products 33 | .OrderBy("Weird Property") 34 | .ToList(); 35 | }); 36 | 37 | Assert.Throws(() => 38 | { 39 | DbContext 40 | .Products 41 | .OrderByDescending("Weird Property") 42 | .ToList(); 43 | }); 44 | } 45 | 46 | [Fact] 47 | public void OrderById() 48 | { 49 | DbContext 50 | .Products 51 | .OrderById(); 52 | } 53 | 54 | [Fact] 55 | public void FirstOrDefaultById() 56 | { 57 | DbContext 58 | .Products 59 | .FirstOrDefaultById(1, x => new ProductListItem() 60 | { 61 | Id = x.Id 62 | }); 63 | } 64 | 65 | public static IEnumerable OrderData => TestCaseBuilder 66 | .For>() 67 | .Add("Name", x => OrderChecker.CheckOrder(x, true)) 68 | .Add("Name asc", x => OrderChecker.CheckOrder(x, true)) 69 | .Add("Name.asc", x => OrderChecker.CheckOrder(x, true)) 70 | .Add("naMe AsC", x => OrderChecker.CheckOrder(x, true)) 71 | .Add("Name desc", x => OrderChecker.CheckOrder(x, false)) 72 | .Add("naMe.DesC", x => OrderChecker.CheckOrder(x, false)); 73 | 74 | 75 | [Theory] 76 | [MemberData(nameof(OrderData))] 77 | public void Order(TestCase> testCase) 78 | { 79 | Sorter.TryParse(testCase.Input, out var sorter); 80 | 81 | var res = DbContext 82 | .Products 83 | .Sort(sorter) 84 | .ToList(); 85 | 86 | testCase.Assert(res); 87 | } 88 | 89 | [Fact] 90 | public void FilterByConventions_ThrowsArgument() 91 | { 92 | Assert.Throws(() => 93 | { 94 | DbContextFixture 95 | .DbContext 96 | .Products 97 | .FilterByConventions(null); 98 | }); 99 | } 100 | 101 | public static IEnumerable FilterData => TestCaseBuilder 102 | .For>() 103 | .Add(new ProductFilter(), x => x.Any()) 104 | .Add(new ProductFilter() 105 | { 106 | Id = 1, 107 | Name = DbContextFixture.FirstProductName 108 | }, x => x.Any() && x.All(y => y.Name.StartsWith(DbContextFixture.FirstProductName))) 109 | .Add(new ProductFilter() 110 | { 111 | Id = 1, 112 | }, x => x.Any() && x.All(y => y.Id == 1)) 113 | .Add(new ProductFilter() 114 | { 115 | Id = 2, 116 | Name = "Not" + DbContextFixture.SecondProductName 117 | }, x => !x.Any()) 118 | .Add(new ProductFilter() 119 | { 120 | Id = 2, 121 | }, x => x.Any()) 122 | ; 123 | 124 | [Theory] 125 | [MemberData(nameof(FilterData))] 126 | public void FilterByConventions(TestCase> testCase) 127 | { 128 | var res = DbContextFixture 129 | .DbContext 130 | .Products 131 | .FilterByConventions(testCase.Input) 132 | .ToList(); 133 | 134 | testCase.Assert(res); 135 | } 136 | } 137 | } -------------------------------------------------------------------------------- /src/Force.Tests/Expressions/SorterTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Linq; 3 | using Force.Tests.Infrastructure.Context; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Expressions 7 | { 8 | public class SorterTests: DbContextFixtureTestsBase 9 | { 10 | public SorterTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 11 | { 12 | } 13 | 14 | [Theory] 15 | [InlineData(true)] 16 | [InlineData(false)] 17 | public void Name_AscDesc(bool asc) 18 | { 19 | var sorter = new Sorter(x => x.Name, asc); 20 | var res = DbContext 21 | .Products 22 | .Sort(sorter) 23 | .ToList(); 24 | 25 | OrderChecker.CheckOrder(res, asc); 26 | } 27 | 28 | [Theory] 29 | [InlineData(true)] 30 | [InlineData(false)] 31 | public void Name_ToExpressions_AscDesc(bool asc) 32 | { 33 | var sorter = new Sorter(x => x.Name, asc); 34 | var res = DbContext 35 | .Products 36 | .OrderBy(sorter) 37 | .ToList(); 38 | 39 | OrderChecker.CheckOrder(res, asc); 40 | } 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /src/Force.Tests/ExpressionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using BenchmarkDotNet.Attributes; 6 | using BenchmarkDotNet.Reports; 7 | using BenchmarkDotNet.Running; 8 | using Force.Expressions; 9 | using Force.Extensions; 10 | using Force.Tests.Expressions; 11 | using Xunit; 12 | 13 | namespace Force.Tests 14 | { 15 | public class ExpressionsTests 16 | { 17 | public const int CompileMeanPlusStdDev = 80000; 18 | public const int AsFuncMeanPlusStdDev = 25; 19 | 20 | [Fact] 21 | public void AsFunc_Caches() 22 | { 23 | var summary = BenchmarkRunner.Run(); 24 | 25 | AssertMean(summary, nameof(ExpressionCompilerBenchmark.Compile), CompileMeanPlusStdDev); 26 | AssertMean(summary, nameof(ExpressionCompilerBenchmark.AsFunc), AsFuncMeanPlusStdDev, true); 27 | } 28 | 29 | private static void AssertMean(Summary summary, string benchmarkName, int mean, 30 | bool assertZeroAllocations = false) 31 | { 32 | var report = GetReportByName(summary, benchmarkName); 33 | Assert.True(report.ResultStatistics.Mean < mean, 34 | $"Mean: {report.ResultStatistics.Mean}"); 35 | 36 | if (assertZeroAllocations) 37 | { 38 | Assert.Equal(0, report.GcStats.Gen0Collections); 39 | Assert.Equal(0, report.GcStats.Gen1Collections); 40 | Assert.Equal(0, report.GcStats.Gen2Collections); 41 | } 42 | } 43 | 44 | private static BenchmarkReport GetReportByName(Summary summary, string benchmarkName) => 45 | summary 46 | .Reports 47 | .First(x => x.BenchmarkCase.Descriptor.DisplayInfo == 48 | nameof(ExpressionCompilerBenchmark) + "." + benchmarkName); 49 | private static double ElapsedNanoSeconds(Stopwatch sw) 50 | { 51 | return (double)sw.ElapsedTicks / Stopwatch.Frequency * 1000000000; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/Force.Tests/Extensions/EnumExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Force.Extensions; 3 | using Xunit; 4 | 5 | namespace Force.Tests.Extensions 6 | { 7 | public enum TestEnum 8 | { 9 | One, 10 | [Display(Name = "Three")] 11 | Two 12 | } 13 | 14 | public class EnumExtensionsTests 15 | { 16 | [Fact] 17 | public void GetDisplayName() 18 | { 19 | Assert.Equal("One", TestEnum.One.GetDisplayName()); 20 | Assert.Equal("Three", TestEnum.Two.GetDisplayName()); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Force.Tests/Extensions/FunctionalExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Threading.Tasks; 4 | using Force.Ddd; 5 | using Force.Extensions; 6 | using Force.Tests.Infrastructure.Context; 7 | using Xunit; 8 | 9 | namespace Force.Tests.Extensions 10 | { 11 | public class FunctionalExtensionsTests 12 | { 13 | [Fact] 14 | public async Task AwaitAndPipeTo() 15 | { 16 | var res = await Task.FromResult(1).AwaitAndPipeTo(x => x + 1); 17 | Assert.Equal(2, res); 18 | } 19 | 20 | [Fact] 21 | public void EnsureInvariant() 22 | { 23 | Assert.Throws(() => 24 | { 25 | var pr = new Product(null, null); 26 | }); 27 | } 28 | 29 | [Fact] 30 | public void EitherOr_Func() 31 | { 32 | Product product = null; 33 | Assert.Equal("true", true.EitherOr(x => "true", x => "false")); 34 | Assert.Equal("false", product.EitherOr(x => "true", x => "false")); 35 | Assert.Equal("true", product.EitherOr(true, x => "true", x => "false")); 36 | Assert.Equal("false", product.EitherOr(false, x => "true", x => "false")); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/Force.Tests/Extensions/ValidationResultExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Force.Ccc; 3 | using Force.Ddd; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Extensions 7 | { 8 | public class ValidationResultExtensionTests 9 | { 10 | [Fact] 11 | public void Success() 12 | { 13 | ValidationResult[] results = { 14 | ValidationResult.Success, 15 | ValidationResult.Success, 16 | }; 17 | 18 | Assert.True(results.IsValid()); 19 | } 20 | 21 | [Fact] 22 | public void Failure() 23 | { 24 | ValidationResult[] results = { 25 | ValidationResult.Success, 26 | new ValidationResult("Error"), 27 | }; 28 | 29 | Assert.False(results.IsValid()); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Force.Tests/Force.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netcoreapp3.1 4 | false 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/Category.cs: -------------------------------------------------------------------------------- 1 | using Force.Ddd; 2 | using Force.Extensions; 3 | 4 | namespace Force.Tests.Infrastructure.Context 5 | { 6 | public class Category: HasNameBase 7 | { 8 | public Category(string name) : base(name) 9 | { 10 | this.EnsureInvariant(); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/DbContextFixture.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Force.Tests.Infrastructure.Context 4 | { 5 | public class DbContextFixture 6 | { 7 | public const string FirstCategoryName = "C1"; 8 | 9 | public const string FirstProductName = "P1"; 10 | 11 | public const string SecondProductName = "P2"; 12 | 13 | public const string LastProductName = "PL"; 14 | 15 | public const int LastProductId = 10; 16 | 17 | public TestsDbContext DbContext { get; } 18 | 19 | static DbContextFixture() 20 | { 21 | var optionsBuilder = new DbContextOptionsBuilder(); 22 | optionsBuilder.UseInMemoryDatabase("Force"); 23 | var dbContext = new TestsDbContext(optionsBuilder.Options); 24 | 25 | var category = new Category(FirstCategoryName); 26 | dbContext.Products.Add(new Product(category, FirstProductName) 27 | { 28 | Id = 1, 29 | }); 30 | 31 | dbContext.Products.Add(new Product(category, SecondProductName) 32 | { 33 | Id = 2, 34 | }); 35 | 36 | dbContext.Products.Add(new Product(category, "P3") 37 | { 38 | Id = 3, 39 | }); 40 | 41 | dbContext.Products.Add(new Product(category, LastProductName) 42 | { 43 | Id = 10, 44 | }); 45 | // 46 | // dbContext.Products.Add(new Product() 47 | // { 48 | // Id = 2, 49 | // Name = "123" 50 | // }); 51 | // 52 | // dbContext.Products.Add(new Product() 53 | // { 54 | // Id = 3, 55 | // Name = "Abc" 56 | // }); 57 | 58 | dbContext.SaveChanges(); 59 | } 60 | 61 | public DbContextFixture() 62 | { 63 | var optionsBuilder = new DbContextOptionsBuilder(); 64 | optionsBuilder.UseInMemoryDatabase("Force"); 65 | DbContext = new TestsDbContext(optionsBuilder.Options); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/DbContextFixtureTestsBase.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace Force.Tests.Infrastructure.Context 4 | { 5 | public class DbContextFixtureTestsBase : IClassFixture 6 | { 7 | protected DbContextFixture DbContextFixture; 8 | 9 | public TestsDbContext DbContext => DbContextFixture.DbContext; 10 | 11 | public DbContextFixtureTestsBase(DbContextFixture dbContextFixture) 12 | { 13 | DbContextFixture = dbContextFixture; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/Product.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Force.Ddd; 3 | using Force.Extensions; 4 | using Force.Linq; 5 | 6 | namespace Force.Tests.Infrastructure.Context 7 | { 8 | [Display(Name = "Product")] 9 | public class Product : HasNameBase 10 | { 11 | [Required] 12 | public Category Category { get; protected set; } 13 | 14 | [SearchBy] 15 | [Required, MinLength(1), MaxLength(Strings.DefaultLength)] 16 | public override string Name { get; protected set; } 17 | 18 | protected Product() : base() 19 | { 20 | } 21 | 22 | public Product(Category category, string name) : base(name) 23 | { 24 | Category = category; 25 | this.EnsureInvariant(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/ProductFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Cqrs; 3 | using Force.Ddd; 4 | using Force.Linq; 5 | 6 | namespace Force.Tests.Infrastructure.Context 7 | { 8 | public class ProductFilter : FilterQuery 9 | { 10 | public int? Id { get; set; } 11 | 12 | public string Name { get; set; } 13 | 14 | public string OrderBy { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/ProductListItem.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Force.Ddd; 3 | 4 | namespace Force.Tests.Infrastructure.Context 5 | { 6 | [Display(Name = "Product List")] 7 | public class ProductListItem: HasIdBase 8 | { 9 | public string Name { get; set; } 10 | 11 | public string CategoryName { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/Context/TestsDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace Force.Tests.Infrastructure.Context 5 | { 6 | public class TestsDbContext : DbContext 7 | { 8 | public DbSet Products { get; set; } 9 | 10 | public DbSet Categories { get; set; } 11 | 12 | public TestsDbContext(DbContextOptions options) : base(options) 13 | { 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/PagedProductFilter.cs: -------------------------------------------------------------------------------- 1 | using Force.Cqrs; 2 | using Force.Tests.Infrastructure.Context; 3 | 4 | namespace Force.Tests.Infrastructure 5 | { 6 | public class PagedProductFilter : PagedFilterQuery 7 | { 8 | public string Name { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/TestCase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Tests.Infrastructure 4 | { 5 | public class TestCase 6 | { 7 | private Func _assertFunc; 8 | 9 | public TestCase(TIn input, Func assertFunc, string errorMessage = null) 10 | { 11 | Input = input; 12 | _assertFunc = assertFunc ?? throw new ArgumentNullException(nameof(assertFunc)); 13 | ErrorMessage = errorMessage; 14 | } 15 | 16 | public static implicit operator TestCase(Tuple> tuple) 17 | => new TestCase(tuple.Item1, tuple.Item2); 18 | 19 | public TIn Input { get; } 20 | 21 | private string ErrorMessage { get; } 22 | 23 | public void Assert(TOut output) 24 | { 25 | var flag = _assertFunc(output); 26 | Xunit.Assert.True(flag, ErrorMessage); 27 | } 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /src/Force.Tests/Infrastructure/TestCaseBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace Force.Tests.Infrastructure 7 | { 8 | public static class TestCaseBuilder 9 | { 10 | public static TestCaseBuilder For() 11 | => new TestCaseBuilder(); 12 | 13 | } 14 | 15 | public class TestCaseBuilder: IEnumerable 16 | { 17 | List> testCases = new List>(); 18 | 19 | public TestCaseBuilder Add(TIn input, Func assert, string errorMessage = null) 20 | { 21 | testCases.Add(new TestCase(input, assert, errorMessage)); 22 | return this; 23 | } 24 | 25 | private IEnumerator DoGetEnumerator() 26 | => testCases 27 | .Select(x => new object[] {x}) 28 | .ToList() 29 | .GetEnumerator(); 30 | 31 | public IEnumerator GetEnumerator() 32 | => DoGetEnumerator(); 33 | 34 | IEnumerator IEnumerable.GetEnumerator() 35 | => DoGetEnumerator(); 36 | } 37 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/FilterConventionsTests/EnumerableConventionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Cqrs; 3 | using Force.Linq; 4 | using Force.Tests.Infrastructure.Context; 5 | using Xunit; 6 | 7 | namespace Force.Tests.Linq 8 | { 9 | public class EnumerableConventionTests : DbContextFixtureTestsBase 10 | { 11 | public EnumerableConventionTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void Test_Filter() 17 | { 18 | var products = DbContext 19 | .Products 20 | .Select(x => new ProductListItem() 21 | { 22 | Name = x.Name, 23 | CategoryName = x.Category.Name 24 | }); 25 | var product = products.FirstOrDefault(); 26 | Assert.NotNull(product); 27 | var categoryName = product.CategoryName; 28 | 29 | var predicate = new ProductListItemFilter() 30 | { 31 | CategoryName = new []{categoryName} 32 | }; 33 | 34 | var actualFilteredProducts = products 35 | .Filter(predicate) 36 | .ToList(); 37 | 38 | Assert.All(actualFilteredProducts, x => Assert.Equal(categoryName, x.CategoryName)); 39 | } 40 | } 41 | 42 | public class ProductListItemFilter : FilterQuery 43 | { 44 | public string[] CategoryName { get; set; } 45 | } 46 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/FilterConventionsTests/FilterConventionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Force.Linq; 3 | using Force.Linq.Conventions; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Linq 7 | { 8 | public class FilterConventionsTests 9 | { 10 | [Fact] 11 | public void Instance() 12 | { 13 | var i = FilterConventions.Instance; 14 | var i2 = FilterConventions.Instance; 15 | 16 | Assert.Equal(i, i2); 17 | } 18 | 19 | [Fact] 20 | public void DoubleInitialization_ThrowsInvalidArgumentException() 21 | { 22 | var i = FilterConventions.Instance; 23 | Assert.Throws(() => 24 | { 25 | FilterConventions.Initialize(); 26 | }); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/PagedEnumerableTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq; 3 | using Force.Linq.Pagination; 4 | using Xunit; 5 | 6 | namespace Force.Tests.Linq 7 | { 8 | public class PagedEnumerableTests 9 | { 10 | [Fact] 11 | public void New() 12 | { 13 | var arr = new[] {"1", "2", "3"}; 14 | var pe = new PagedEnumerable(arr, arr.Length); 15 | var enm = pe.GetEnumerator(); 16 | var enm2 = ((IEnumerable) pe).GetEnumerator(); 17 | var t = pe.Total; 18 | 19 | Assert.Equal(arr.Length, t); 20 | 21 | var count = 0; 22 | while (enm.MoveNext()) 23 | { 24 | Assert.Contains(enm.Current, arr); 25 | count++; 26 | } 27 | 28 | Assert.Equal(arr.Length, count); 29 | 30 | count = 0; 31 | while (enm2.MoveNext()) 32 | { 33 | Assert.Contains(enm2.Current, arr); 34 | count++; 35 | } 36 | 37 | Assert.Equal(arr.Length, count); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/PagingTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Force.Linq.Pagination; 4 | using Force.Tests.Expressions; 5 | using Force.Tests.Infrastructure.Context; 6 | using Xunit; 7 | 8 | namespace Force.Tests.Linq 9 | { 10 | public class PagingTests: DbContextFixtureTestsBase 11 | { 12 | public PagingTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 13 | { 14 | } 15 | 16 | [Fact] 17 | public void PageIsZero_ThrowsArgumentException() 18 | { 19 | Assert.Throws(() => 20 | { 21 | var p = new Paging(1, 1) 22 | { 23 | Page = 0, 24 | Take = 1 25 | }; 26 | }); 27 | } 28 | 29 | [Fact] 30 | public void TakeIsZero_ThrowsArgumentException() 31 | { 32 | Assert.Throws(() => 33 | { 34 | var p = new Paging(1, 1) 35 | { 36 | Page = 1, 37 | Take = 0 38 | }; 39 | }); 40 | } 41 | 42 | [Fact] 43 | public void Total() 44 | { 45 | var paging = new Paging(); 46 | paging = new Paging(1, 1) 47 | { 48 | Page = 1, 49 | Take = 1 50 | }; 51 | 52 | var res = DbContext 53 | .Products 54 | .OrderBy(x => x.Id) 55 | .ToPagedEnumerable(paging); 56 | 57 | Assert.Single(res.Items); 58 | Assert.Equal(DbContext.Products.Count(), res.Total); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/QueryableExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Linq; 3 | using Force.Tests.Cqrs; 4 | using Force.Tests.Infrastructure; 5 | using Force.Tests.Infrastructure.Context; 6 | using Xunit; 7 | 8 | namespace Force.Tests.Linq 9 | { 10 | public class QueryableExtensionsTests: DbContextFixtureTestsBase 11 | { 12 | public QueryableExtensionsTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 13 | { 14 | } 15 | 16 | private static readonly string[] Strings = {"1", "2", "3"}; 17 | 18 | public IQueryable Queryable = Strings.AsQueryable(); 19 | 20 | [Fact] 21 | public void FilterByConventions_Empty() 22 | { 23 | var totalCount = DbContext 24 | .Products 25 | .Count(); 26 | 27 | var res = DbContext 28 | .Products 29 | .FilterByConventions(new PagedProductFilter()) 30 | .Count(); 31 | 32 | Assert.Equal(totalCount, res); 33 | } 34 | 35 | [Fact] 36 | public void FilterSortAndPaginate() 37 | { 38 | var f = new PagedProductFilter() 39 | { 40 | Asc = false, 41 | Order = "Name", 42 | Page = 1, 43 | Take = 2 44 | }; 45 | 46 | var res = DbContext 47 | .Products 48 | .Filter(f) 49 | .FilterSortAndPaginate(f); 50 | 51 | Assert.Equal(2, res.Count()); 52 | Assert.Equal(DbContext.Products.Count(), res.Total); 53 | } 54 | 55 | [Theory] 56 | [InlineData(1)] 57 | [InlineData(2)] 58 | [InlineData(3)] 59 | public void FirstOrDefaultById_Projection(int id) 60 | { 61 | var actual = DbContext 62 | .Products 63 | .FirstOrDefaultById(id, x => new ProductListItem() 64 | { 65 | Id = x.Id, 66 | Name = x.Name, 67 | CategoryName = x.Category.Name 68 | }); 69 | 70 | var expected = DbContext 71 | .Products 72 | .Where(x => x.Id == id) 73 | .Select(x => new ProductListItem() 74 | { 75 | Id = x.Id, 76 | Name = x.Name, 77 | CategoryName = x.Category.Name 78 | }) 79 | .FirstOrDefault(); 80 | 81 | Assert.Equal(expected.Id, actual.Id); 82 | Assert.Equal(expected.Name, actual.Name); 83 | Assert.Equal(expected.CategoryName, actual.CategoryName); 84 | } 85 | 86 | [Fact] 87 | public void FirstOrDefaultById() 88 | { 89 | DbContext 90 | .Products 91 | .WhereIf(false, x => true) 92 | .WhereIf(false, x => true, x => true) 93 | .WhereIfNotNull(null, x => true) 94 | .OrderById() 95 | .ById(0) 96 | .FirstOrDefaultById(0); 97 | } 98 | 99 | 100 | [Fact] 101 | public void ById() 102 | { 103 | 104 | } 105 | 106 | [Fact] 107 | public void WhereIf() 108 | { 109 | Queryable.WhereIf(true, x => true); 110 | Queryable.WhereIf(false, x => true); 111 | 112 | Queryable.WhereIf(true, x => true, x => false); 113 | } 114 | 115 | [Fact] 116 | public void OrderBy() 117 | { 118 | // try? 119 | Queryable.OrderBy("Length"); 120 | } 121 | 122 | [Fact] 123 | public void OrderByDescending() 124 | { 125 | Queryable.OrderByDescending("Length"); 126 | } 127 | } 128 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/SearchByTests.cs: -------------------------------------------------------------------------------- 1 | using Force.Linq; 2 | using Xunit; 3 | 4 | namespace Force.Tests.Linq 5 | { 6 | public class SearchByTests 7 | { 8 | [Theory] 9 | [InlineData(SearchKind.Contains)] 10 | [InlineData(SearchKind.StartsWith)] 11 | public void New(SearchKind searchKind) 12 | { 13 | var sba = new SearchByAttribute(searchKind); 14 | Assert.Equal(searchKind, sba.SearchKind); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Force.Tests/Linq/SorterTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Force.Linq; 4 | using Force.Tests.Infrastructure.Context; 5 | using Xunit; 6 | 7 | namespace Force.Tests.Linq 8 | { 9 | public class SorterTests: DbContextFixtureTestsBase 10 | { 11 | public SorterTests(DbContextFixture dbContextFixture) : base(dbContextFixture) 12 | { 13 | } 14 | 15 | [Fact] 16 | public void New() 17 | { 18 | Assert.Throws(() => 19 | { 20 | var s = new Sorter("Wrong"); 21 | }); 22 | } 23 | 24 | [Fact] 25 | public void TryParse_PropertyExists_AscNotNull() 26 | { 27 | Assert.True(Sorter.TryParse("Length", out var s)); 28 | Assert.NotNull(s); 29 | Assert.True(s.IsAsc); 30 | } 31 | 32 | [Fact] 33 | public void TryParse_PropertyExists_DescNotNull() 34 | { 35 | Assert.True(Sorter.TryParse("Length Desc", out var s)); 36 | Assert.NotNull(s); 37 | Assert.False(s.IsAsc); 38 | } 39 | 40 | 41 | [Theory] 42 | [InlineData("Length W")] 43 | [InlineData("Abyrwalg")] 44 | public void TryParse_PropertyDoesntExist_Null(string propertyName) 45 | { 46 | Assert.False(Sorter.TryParse(propertyName, out var s)); 47 | Assert.Null(s); 48 | } 49 | 50 | 51 | [Fact] 52 | public void Sort() 53 | { 54 | var s = new Sorter("Id", false); 55 | var sorted = s 56 | .Sort(DbContext.Products) 57 | .ToList(); 58 | 59 | Assert.True(sorted.Count > 2); 60 | var flag = true; 61 | for (var i = 0; i < sorted.Count - 2; i++) 62 | { 63 | if (sorted[i].Id < sorted[i + 1].Id) 64 | { 65 | flag = false; 66 | break; 67 | } 68 | } 69 | 70 | Assert.True(flag); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/Force.Tests/QueryTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Extensions; 3 | using Force.Tests.Infrastructure.Context; 4 | using Xunit; 5 | 6 | namespace Force.Tests 7 | { 8 | public class QueryTests: DbContextFixture 9 | { 10 | [Fact] 11 | public void PagedQuery_FilterAndSort() 12 | { 13 | // var products = DbContext.Products.FilterAndSort(new PagedQuery()); 14 | // Assert.True(products.First().Id == 1); 15 | } 16 | 17 | [Fact] 18 | public void PagedQuery_FilterSortAndPaginate() 19 | { 20 | // var products = DbContext.Products.FilterSortAndPaginate(new PagedQuery()); 21 | // Assert.Equal(2, products.Total); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/Force.Tests/Reflection/TypeTests.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.Linq; 3 | using Force.Extensions; 4 | using Force.Reflection; 5 | using Force.Tests.Expressions; 6 | using Force.Tests.Infrastructure.Context; 7 | using Xunit; 8 | 9 | namespace Force.Tests.Reflection 10 | { 11 | public class TypeTests 12 | { 13 | [Fact] 14 | public void PropertyGetter() 15 | { 16 | // var getter = Type.PropertyGetter("Name"); 17 | // var func = getter.Compile(); 18 | // var name = func(new Product() {Name = "Product Name"}); 19 | // Assert.Equal("Product Name", name); 20 | } 21 | 22 | [Fact] 23 | public void HasAttribute() 24 | { 25 | var hasDisplayAttribute = Type.HasAttribute(); 26 | Assert.True(hasDisplayAttribute); 27 | 28 | var name = Type 29 | .Attributes 30 | .First(x => typeof(DisplayAttribute) == x.GetType()) 31 | .PipeTo(x => (DisplayAttribute) x) 32 | .Name; 33 | 34 | Assert.Equal("Product List", name); 35 | } 36 | 37 | [Fact] 38 | public void PublicMethods() 39 | { 40 | var methods = Type.PublicMethods; 41 | var expectedMetods = typeof(Product) 42 | .GetMethods() 43 | .Where(x => x.IsPublic && !x.IsAbstract) 44 | .ToArray(); 45 | 46 | Assert.All(methods, x => expectedMetods.Contains(x)); 47 | Assert.All(expectedMetods, x => methods.Contains(x)); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/Force.Tests/SpecBuilderTests.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Tests 2 | { 3 | public class SpecBuilderTests 4 | { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /src/Force.Tests/SpecTests.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using Force.Ddd; 3 | using Xunit; 4 | 5 | namespace Force.Tests 6 | { 7 | public class SpecTests 8 | { 9 | [Fact] 10 | public void A() 11 | { 12 | var spec = new Spec(x => x.ToString().Length < 5); 13 | 14 | var sw = new Stopwatch(); 15 | sw.Start(); 16 | 17 | spec.IsSatisfiedBy("string"); 18 | var e1 = sw.Elapsed; 19 | sw.Restart(); 20 | 21 | var e2 = sw.Elapsed; 22 | spec.IsSatisfiedBy("string"); 23 | 24 | Assert.True(e2 * 10 < e1); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/Force.Tests/TypeTestObject.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Tests 2 | { 3 | public class TypeTestObject 4 | { 5 | public TypeTestObject(int i11) 6 | { 7 | } 8 | 9 | public TypeTestObject(int i11, int i2) 10 | { 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force.Tests/TypeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using Force.Ddd; 5 | using Force.Reflection; 6 | using Force.Tests.Expressions; 7 | using Force.Tests.Infrastructure.Context; 8 | using Xunit; 9 | 10 | namespace Force.Tests 11 | { 12 | public class SimpeValueObject : ValueObject 13 | { 14 | public SimpeValueObject(string value) : base(value) 15 | { 16 | } 17 | } 18 | 19 | public class TypeTests 20 | { 21 | [Fact] 22 | public void GetConstructorInfo_FindConstructor() 23 | { 24 | var ctr = Type.GetConstructorInfo(new object[] {1, 2}); 25 | Assert.NotNull(ctr); 26 | } 27 | 28 | [Fact] 29 | public void GetConstructorInfo_ThrowsInvalidOperation() 30 | { 31 | Assert.Throws(() => 32 | { 33 | Type.GetConstructorInfo(new object[] {"1", 2}); 34 | }); 35 | } 36 | 37 | [Fact] 38 | public void TryGetValue() 39 | { 40 | var productFilter = new ProductFilter(){Name = "123"}; 41 | productFilter.TryGetValue("Name", out var val); 42 | } 43 | 44 | [Fact] 45 | public void TryGetValue_Null() 46 | { 47 | throw new NotImplementedException(); 48 | } 49 | 50 | [Fact] 51 | public void Create() 52 | { 53 | var vo = Type.CreateInstance("string"); 54 | } 55 | 56 | [Fact] 57 | public void GetCustomAttribute() 58 | { 59 | var attr = Type.GetCustomAttribute(); 60 | Assert.NotNull(attr); 61 | } 62 | 63 | [Fact] 64 | public void PropertySetter() 65 | { 66 | var setter = Type.PropertySetter(nameof(Product.Name)); 67 | Assert.NotNull(setter); 68 | } 69 | 70 | [Fact] 71 | public void PropertyGetter() 72 | { 73 | var getter = Type.PropertyGetter(nameof(Product.Name)); 74 | Assert.NotNull(getter); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/Force.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.10 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Force", "Force\Force.csproj", "{FB262EF5-16F5-47ED-AA59-327E923875D4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Force.Tests", "Force.Tests\Force.Tests.csproj", "{64E5449A-CB76-4F6E-B589-371FFB9A064F}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FB262EF5-16F5-47ED-AA59-327E923875D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {FB262EF5-16F5-47ED-AA59-327E923875D4}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {FB262EF5-16F5-47ED-AA59-327E923875D4}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {FB262EF5-16F5-47ED-AA59-327E923875D4}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {64E5449A-CB76-4F6E-B589-371FFB9A064F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {64E5449A-CB76-4F6E-B589-371FFB9A064F}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {64E5449A-CB76-4F6E-B589-371FFB9A064F}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {64E5449A-CB76-4F6E-B589-371FFB9A064F}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(NestedProjects) = preSolution 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /src/Force/Ccc/IHasUserFrendlyMessage.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Ccc 2 | { 3 | public interface IHasUserFrendlyMessage 4 | { 5 | string Message { get; } 6 | } 7 | } -------------------------------------------------------------------------------- /src/Force/Ccc/IPermissionFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Force.Ccc 4 | { 5 | public interface IPermissionFilter 6 | { 7 | IQueryable GetPermitted(IQueryable queryable); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Force/Ccc/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Transactions; 3 | using Force.Ddd; 4 | 5 | namespace Force.Ccc 6 | { 7 | public interface IUnitOfWork : IDisposable 8 | { 9 | void Add(TEntity entity) 10 | where TEntity : class, IHasId; 11 | 12 | void Remove(TEntity entity) 13 | where TEntity : class, IHasId; 14 | 15 | TEntity Find(params object[] id); 16 | 17 | void Commit(); 18 | 19 | IUnitOfWorkTransaction BeginTransaction(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Force/Ccc/IUnitOfWorkTransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Force.Ccc 6 | { 7 | public interface IUnitOfWorkTransaction : IDisposable 8 | { 9 | /// 10 | /// Gets the transaction identifier. 11 | /// 12 | Guid TransactionId { get; } 13 | 14 | /// 15 | /// Commits all changes made to the database in the current transaction. 16 | /// 17 | void Commit(); 18 | 19 | /// 20 | /// Discards all changes made to the database in the current transaction. 21 | /// 22 | void Rollback(); 23 | 24 | /// 25 | /// Commits all changes made to the database in the current transaction asynchronously. 26 | /// 27 | /// The cancellation token. 28 | /// A representing the asynchronous operation. 29 | Task CommitAsync(CancellationToken cancellationToken = default); 30 | 31 | /// 32 | /// Discards all changes made to the database in the current transaction asynchronously. 33 | /// 34 | /// The cancellation token. 35 | /// A representing the asynchronous operation. 36 | Task RollbackAsync(CancellationToken cancellationToken = default); 37 | } 38 | } -------------------------------------------------------------------------------- /src/Force/Ccc/IValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Linq; 4 | 5 | namespace Force.Ccc 6 | { 7 | public interface IValidator 8 | { 9 | IEnumerable Validate(T obj); 10 | } 11 | 12 | public static class ValidationResultExtensions 13 | { 14 | public static bool IsValid(this IEnumerable results) 15 | => results == null || results.All(x => x == ValidationResult.Success); 16 | } 17 | } -------------------------------------------------------------------------------- /src/Force/Ccc/Result.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Ccc 4 | { 5 | public class Result 6 | { 7 | private readonly TFailure _failure; 8 | private readonly TSuccess _success; 9 | private readonly bool _isSuccess; 10 | 11 | public static implicit operator Result (TFailure failure) 12 | => new Result(failure); 13 | 14 | public static implicit operator Result (TSuccess success) 15 | => new Result(success); 16 | 17 | public Result(TSuccess success) 18 | { 19 | _success = success; 20 | _isSuccess = true; 21 | } 22 | 23 | public Result(TFailure failure) 24 | { 25 | _failure = failure; 26 | } 27 | 28 | public bool IsFaulted => !_isSuccess; 29 | 30 | public TResult Match(Func success, Func failure) 31 | => _isSuccess ? success(_success) : failure(_failure); 32 | } 33 | 34 | public class Result: Result 35 | { 36 | public Result(T success) : base(success) 37 | { 38 | } 39 | 40 | public Result(string failure) : base(failure) 41 | { 42 | } 43 | 44 | public Result(Exception e) : base(e.Message) 45 | { 46 | } 47 | } 48 | 49 | public static class ResultExtensions 50 | { 51 | public static Result Select( 52 | this Result source, 53 | Func selector) 54 | => source.Match>(x => selector(x), x => x); 55 | 56 | public static Result SelectMany( 57 | this Result source, 58 | Func> selector) 59 | => source.Match(selector, x => x); 60 | 61 | public static Result 62 | SelectMany( 63 | this Result result, 64 | Func> inermidiateSelector, 65 | Func resultSelector) 66 | => result 67 | .SelectMany(s => inermidiateSelector(s) 68 | .SelectMany(m => resultSelector(s, m))); 69 | } 70 | } -------------------------------------------------------------------------------- /src/Force/Ccc/UnitOfWorkBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Force.Cqrs; 3 | using Force.Ddd; 4 | using Force.Ddd.DomainEvents; 5 | 6 | namespace Force.Ccc 7 | { 8 | public abstract class UnitOfWorkBase : IUnitOfWork 9 | { 10 | private readonly IHandler> _domainEventDispatcher; 11 | 12 | protected UnitOfWorkBase(IHandler> domainEventDispatcher) 13 | { 14 | _domainEventDispatcher = domainEventDispatcher; 15 | } 16 | 17 | public abstract void Dispose(); 18 | 19 | public abstract void Add(TEntity entity) 20 | where TEntity : class, IHasId; 21 | 22 | public abstract void Remove(TEntity entity) 23 | where TEntity : class, IHasId; 24 | 25 | public abstract TEntity Find(params object[] id); 26 | 27 | public void Commit() 28 | { 29 | var events = GetDomainEvents(); 30 | _domainEventDispatcher.Handle(events); 31 | DoCommit(); 32 | } 33 | 34 | public abstract IUnitOfWorkTransaction BeginTransaction(); 35 | 36 | public abstract void Update(TEntity entity) 37 | where TEntity : class, IHasId; 38 | 39 | protected abstract void DoCommit(); 40 | 41 | protected abstract IEnumerable GetDomainEvents(); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/FilterQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Force.Cqrs 4 | { 5 | public class FilterQuery 6 | : FilterQueryBase 7 | , IQuery> 8 | 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/FilterQueryAsync.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Force.Cqrs 5 | { 6 | public class FilterQueryAsync 7 | : FilterQueryBase 8 | , IQuery>> 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/FilterQueryBase.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Ddd; 3 | using Force.Linq; 4 | 5 | namespace Force.Cqrs 6 | { 7 | public class FilterQueryBase 8 | : IFilter 9 | , ISorter 10 | { 11 | public string Search { get; set; } 12 | 13 | public string Order { get; set; } 14 | 15 | public bool Asc { get; set; } = true; 16 | 17 | public virtual IOrderedQueryable Sort(IQueryable queryable) => Order == null 18 | ? queryable.OrderBy(_ => 0) 19 | : Asc 20 | ? queryable.OrderBy(Order) 21 | : queryable.OrderByDescending(Order); 22 | 23 | public virtual IQueryable Filter(IQueryable queryable) 24 | { 25 | // dynamic is for setting Build the right type 26 | var spec = (Spec)SpecBuilder.Build((dynamic)this); 27 | var searchSpec = (Spec)SpecBuilder.BuildSearch((dynamic) this); 28 | return queryable.Where(spec) 29 | .Where(searchSpec); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Force.Cqrs 5 | { 6 | public interface ICommand 7 | { 8 | } 9 | 10 | public interface ICommand 11 | { 12 | } 13 | 14 | public interface IValidatableCommand : ICommand> 15 | { 16 | } 17 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/ICommandHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Force.Cqrs 5 | { 6 | public interface ICommandHandler: IHandler 7 | where T: ICommand 8 | { 9 | } 10 | 11 | public interface ICommandHandler: IHandler 12 | where TIn: ICommand 13 | { 14 | } 15 | 16 | public interface IValidatableCommandHandler : ICommandHandler> 17 | where T: IValidatableCommand 18 | { 19 | } 20 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/IHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Cqrs 2 | { 3 | public interface IHandler 4 | { 5 | void Handle(TIn input); 6 | } 7 | 8 | public interface IHandler 9 | { 10 | TOut Handle(TIn input); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/IQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Cqrs 2 | { 3 | public interface IQuery 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/IQueryHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Cqrs 2 | { 3 | public interface IQueryHandler: IHandler 4 | where TIn: IQuery 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/PagedFilterQuery.cs: -------------------------------------------------------------------------------- 1 | using Force.Linq.Pagination; 2 | 3 | namespace Force.Cqrs 4 | { 5 | public class PagedFilterQuery 6 | : PagedFilterQueryBase 7 | , IQuery> 8 | { 9 | } 10 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/PagedFilterQueryAsync.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Force.Linq.Pagination; 3 | 4 | namespace Force.Cqrs 5 | { 6 | public class PagedFilterQueryAsync 7 | : PagedFilterQueryBase, IQuery>> 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/Force/Cqrs/PagedFilterQueryBase.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using Force.Linq.Pagination; 3 | 4 | namespace Force.Cqrs 5 | { 6 | public class PagedFilterQueryBase 7 | : FilterQueryBase 8 | , IPaging 9 | { 10 | [Range(1, int.MaxValue)] 11 | public int Page { get; set; } = 1; 12 | 13 | [Range(1, int.MaxValue)] 14 | public int Take { get; set; } = 1; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Force/Ddd/DomainEvents/DomainEventStore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace Force.Ddd.DomainEvents 5 | { 6 | public class DomainEventStore: IEnumerable 7 | { 8 | public List _domainEvents = new List(); 9 | 10 | public void Raise(IDomainEvent domainEvent) 11 | { 12 | _domainEvents.Add(domainEvent); 13 | } 14 | 15 | public IEnumerator GetEnumerator() 16 | => _domainEvents.GetEnumerator(); 17 | 18 | IEnumerator IEnumerable.GetEnumerator() 19 | => _domainEvents.GetEnumerator(); 20 | } 21 | } -------------------------------------------------------------------------------- /src/Force/Ddd/DomainEvents/IDomainEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Ddd.DomainEvents 4 | { 5 | public interface IDomainEvent 6 | { 7 | DateTime Happened { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Force/Ddd/DomainEvents/IHasDomainEvents.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Force.Ddd.DomainEvents 4 | { 5 | public interface IHasDomainEvents: IHasDomainEvents 6 | { 7 | } 8 | 9 | public interface IHasDomainEvents 10 | where T: IDomainEvent 11 | { 12 | IEnumerable GetDomainEvents(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/Force/Ddd/Formatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Force.Expressions; 4 | using Force.Extensions; 5 | 6 | namespace Force.Ddd 7 | { 8 | public class Formatter 9 | { 10 | public static implicit operator Formatter (Expression> expresssion) 11 | => new Formatter(expresssion); 12 | 13 | public static implicit operator Expression>(Formatter formatter) => formatter._expression; 14 | 15 | private readonly Expression> _expression; 16 | 17 | public Formatter(Expression> expression) 18 | { 19 | _expression = expression ?? throw new ArgumentNullException(nameof(expression)); 20 | } 21 | 22 | public Formatter From(Expression> mapFrom) 23 | => _expression.From(mapFrom); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Force/Ddd/HasIdBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Force.Ddd 5 | { 6 | public abstract class HasIdBase: HasIdBase 7 | {} 8 | 9 | public abstract class HasIdBase : IHasId 10 | where TKey: IEquatable 11 | { 12 | [Key, Required] 13 | public virtual TKey Id { get; set; } 14 | 15 | object IHasId.Id => Id; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Force/Ddd/HasNameBase.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace Force.Ddd 4 | { 5 | public abstract class HasNameBase : HasIdBase 6 | { 7 | protected HasNameBase() {} 8 | 9 | protected HasNameBase(string name) 10 | { 11 | Name = name; 12 | } 13 | 14 | [Required, MinLength(1), MaxLength(Strings.DefaultLength)] 15 | public virtual string Name { get; protected set; } 16 | } 17 | } -------------------------------------------------------------------------------- /src/Force/Ddd/IHasId.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Ddd 4 | { 5 | public interface IHasId 6 | { 7 | object Id { get; } 8 | } 9 | 10 | public interface IHasId : IHasId 11 | where TKey: IEquatable 12 | { 13 | new TKey Id { get; } 14 | } 15 | 16 | public static class HasIdExtensions 17 | { 18 | public static bool IsNew(this IHasId obj) 19 | where TKey : IEquatable 20 | { 21 | return obj.Id == null || obj.Id.Equals(default); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Force/Ddd/Id.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Ddd 4 | { 5 | public class Id: Id 6 | where T : class, IHasId 7 | { 8 | public static bool TryParse(int value, Func loader, out Id id) 9 | { 10 | if (value <= 0) 11 | { 12 | id = null; 13 | return false; 14 | } 15 | 16 | id = new Id(value, loader); 17 | return true; 18 | } 19 | 20 | public Id(T entity) : base(entity) 21 | { 22 | } 23 | 24 | public Id(int value, Func loader) : base(value, loader) 25 | { 26 | } 27 | 28 | public static implicit operator Id(T entity) 29 | => new Id(entity); 30 | } 31 | 32 | public class Id 33 | where T: class, IHasId 34 | where TKey : IEquatable 35 | { 36 | public Id(T entity) 37 | { 38 | _entity = entity ?? throw new ArgumentNullException(nameof(entity)); 39 | if (entity.IsNew()) 40 | { 41 | throw new ArgumentException("Id must have value", nameof(entity)); 42 | } 43 | 44 | Value = entity.Id; 45 | } 46 | 47 | public Id(TKey value, Func loader) 48 | { 49 | if(Equals(value, default(TKey))) 50 | { 51 | throw new ArgumentException("Id must have value", nameof(value)); 52 | } 53 | 54 | Value = value; 55 | _loader = loader ?? throw new ArgumentNullException(nameof(loader)); 56 | } 57 | 58 | public static bool TryParse(TKey value, Func loader, out Id id) 59 | { 60 | if (Equals(value, default(TKey))) 61 | { 62 | id = null; 63 | return false; 64 | } 65 | 66 | id = new Id(value, loader); 67 | return true; 68 | } 69 | 70 | private T _entity; 71 | 72 | private readonly Func _loader; 73 | 74 | public TKey Value { get; } 75 | 76 | public T Entity => _entity ?? (_entity = _loader(Value)); 77 | 78 | public static implicit operator TKey(Id id) 79 | => id.Value; 80 | 81 | public static implicit operator T(Id id) 82 | => id.Entity; 83 | 84 | public static implicit operator Id(T entity) 85 | => new Id(entity); 86 | } 87 | } -------------------------------------------------------------------------------- /src/Force/Ddd/Spec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using Force.Expressions; 4 | using Force.Extensions; 5 | 6 | namespace Force.Ddd 7 | { 8 | public class Spec 9 | { 10 | public static bool operator false(Spec spec) => false; 11 | 12 | public static bool operator true(Spec spec) => false; 13 | 14 | public static Spec operator &(Spec spec1, Spec spec2) 15 | => new Spec(spec1._expression.And(spec2._expression)); 16 | 17 | public static Spec operator |(Spec spec1, Spec spec2) 18 | => new Spec(spec1._expression.Or(spec2._expression)); 19 | 20 | public static Spec operator !(Spec spec) 21 | => new Spec(spec._expression.Not()); 22 | 23 | public static implicit operator Expression>(Spec spec) 24 | => spec?._expression; 25 | 26 | public static implicit operator Spec(Expression> expression) 27 | => new Spec(expression); 28 | 29 | private readonly Expression> _expression; 30 | 31 | private Func _func; 32 | 33 | public Spec(Expression> expression) 34 | { 35 | _expression = expression ?? throw new ArgumentNullException(nameof(expression)); 36 | } 37 | 38 | public bool IsSatisfiedBy(T obj) => (_func ?? (_func = _expression.AsFunc()))(obj); 39 | 40 | public Spec From(Expression> mapFrom) 41 | => _expression.From(mapFrom); 42 | } 43 | } -------------------------------------------------------------------------------- /src/Force/Ddd/SpecBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using Force.Expressions; 7 | using Force.Linq; 8 | using Force.Linq.Conventions; 9 | using Force.Reflection; 10 | 11 | namespace Force.Ddd 12 | { 13 | public static class SpecBuilder 14 | { 15 | public static Spec Build(TPredicate predicate, ComposeKind composeKind = ComposeKind.And) 16 | => SpecBuilder.Build(predicate, composeKind); 17 | 18 | public static Spec BuildSearch(TPredicate predicate, ComposeKind composeKind = ComposeKind.Or) 19 | => SpecBuilder.BuildSearch(predicate, composeKind); 20 | } 21 | 22 | public static class SpecBuilder 23 | { 24 | // ReSharper disable once StaticMemberInGenericType 25 | private static readonly List SubjectPropertiesToFilter 26 | = Type 27 | .PublicProperties 28 | .Where(x => Type 29 | .PublicProperties 30 | .Keys 31 | .Contains(x.Key)) 32 | .Select(x => x.Value) 33 | .ToList(); 34 | 35 | public static Spec Build(TPredicate predicate, ComposeKind composeKind = ComposeKind.And) 36 | { 37 | var publicProperties = Type.PublicProperties; 38 | 39 | var props = SubjectPropertiesToFilter 40 | .Select(x => new PropertyInfoAndValue 41 | { 42 | Property = x, 43 | Value = publicProperties[x.Name].GetValue(predicate) 44 | }); 45 | 46 | return GetSpec(props, composeKind); 47 | } 48 | 49 | public static Spec BuildSearch(TPredicate predicate, ComposeKind composeKind = ComposeKind.Or) 50 | { 51 | var publicProperties = Type.PublicProperties; 52 | 53 | var props = SubjectPropertiesToFilter 54 | .Select(x => new PropertyInfoAndValue 55 | { 56 | Property = x, 57 | Value = publicProperties["Search"].GetValue(predicate) 58 | }); 59 | 60 | return GetSpec(props, composeKind); 61 | } 62 | 63 | private static Spec GetSpec(IEnumerable props, 64 | ComposeKind composeKind) 65 | { 66 | var parameter = Expression.Parameter(typeof(TSubject)); 67 | 68 | var expressions = props 69 | .Where(x => x.Value != null) 70 | .Select(x => BuildExpression(parameter, x)) 71 | .Where(x => x != null) 72 | .ToList(); 73 | 74 | if (!expressions.Any()) 75 | { 76 | return new Spec(x => true); 77 | } 78 | 79 | var expr = composeKind == ComposeKind.And 80 | ? expressions.Aggregate((c, n) => c.And(n)) 81 | : expressions.Aggregate((c, n) => c.Or(n)); 82 | 83 | return new Spec(expr); 84 | } 85 | 86 | private static Expression> BuildExpression(ParameterExpression parameter, 87 | PropertyInfoAndValue x) 88 | { 89 | var property = Expression.Property(parameter, x.Property); 90 | var val = (x.Value as string)?.ToLower() ?? x.Value; 91 | Expression value = Expression.Constant(val); 92 | 93 | var convention = FilterConventions.Instance.GetConvention(property.Type, x.Value.GetType()); 94 | if (convention == null) 95 | { 96 | return null; 97 | } 98 | 99 | var body = convention.BuildFilterBody(property, value); 100 | return Expression.Lambda>(body, parameter); 101 | } 102 | } 103 | 104 | internal class PropertyInfoAndValue 105 | { 106 | public PropertyInfo Property { get; set; } 107 | 108 | public object Value { get; set; } 109 | } 110 | } -------------------------------------------------------------------------------- /src/Force/Ddd/Strings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Ddd 4 | { 5 | public static class Strings 6 | { 7 | public const int DefaultLength = 255; 8 | } 9 | } -------------------------------------------------------------------------------- /src/Force/Ddd/ValueObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Force.Ddd 6 | { 7 | public abstract class ValueObject 8 | { 9 | protected abstract IEnumerable GetEqualityComponents(); 10 | 11 | public override bool Equals(object obj) 12 | { 13 | if (obj == null) 14 | { 15 | return false; 16 | } 17 | 18 | if (GetType() != obj.GetType()) 19 | { 20 | return false; 21 | } 22 | 23 | var valueObject = (ValueObject)obj; 24 | 25 | return GetEqualityComponents().SequenceEqual(valueObject.GetEqualityComponents()); 26 | } 27 | 28 | public override int GetHashCode() 29 | { 30 | return GetEqualityComponents() 31 | .Aggregate(1, (current, obj) => 32 | { 33 | unchecked 34 | { 35 | return current * 23 + (obj?.GetHashCode() ?? 0); 36 | } 37 | }); 38 | } 39 | 40 | public static bool operator == (ValueObject a, ValueObject b) 41 | { 42 | if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) 43 | return true; 44 | 45 | if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) 46 | return false; 47 | 48 | return a.Equals(b); 49 | } 50 | 51 | public static bool operator != (ValueObject a, ValueObject b) 52 | { 53 | return !(a == b); 54 | } 55 | } 56 | 57 | public abstract class ValueObject : ValueObject 58 | { 59 | protected readonly T Value; 60 | 61 | protected ValueObject(T value) 62 | { 63 | Value = value; 64 | } 65 | 66 | protected override IEnumerable GetEqualityComponents() 67 | { 68 | yield return Value; 69 | } 70 | 71 | public static implicit operator T(ValueObject value) 72 | => value.Value; 73 | } 74 | } -------------------------------------------------------------------------------- /src/Force/Expressions/CompiledExpressions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Linq.Expressions; 4 | 5 | namespace Force.Expressions 6 | { 7 | internal class CompiledExpressions 8 | { 9 | private static readonly ConcurrentDictionary>, Func> Cache 10 | = new ConcurrentDictionary>, Func>(); 11 | 12 | internal static Func AsFunc(Expression> expr) 13 | => Cache.GetOrAdd(expr, k => k.Compile()); 14 | } 15 | } -------------------------------------------------------------------------------- /src/Force/Expressions/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Force.Expressions 5 | { 6 | public static class ExpressionExtensions 7 | { 8 | public static Func AsFunc(this Expression> expr) 9 | => CompiledExpressions.AsFunc(expr); 10 | 11 | public static Expression> From( 12 | this Expression> source, Expression> mapFrom) 13 | => Expression.Lambda>( 14 | Expression.Invoke(source, mapFrom.Body), mapFrom.Parameters); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Force/Expressions/PredicateBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Force.Expressions 7 | { 8 | public static class PredicateBuilder 9 | { 10 | /// 11 | /// Combines the first predicate with the second using the logical "and". 12 | /// 13 | public static Expression> And(this Expression> first, Expression> second) 14 | { 15 | return first.Compose>(second, Expression.AndAlso); 16 | } 17 | 18 | /// 19 | /// Combines the first predicate with the second using the logical "or". 20 | /// 21 | public static Expression> Or(this Expression> first, Expression> second) 22 | { 23 | return first.Compose>(second, Expression.OrElse); 24 | } 25 | 26 | /// 27 | /// Negates the predicate. 28 | /// 29 | public static Expression> Not(this Expression> expression) 30 | { 31 | var negated = Expression.Not(expression.Body); 32 | return Expression.Lambda>(negated, expression.Parameters); 33 | } 34 | 35 | /// 36 | /// Combines the first expression with the second using the specified merge function. 37 | /// 38 | public static Expression Compose(this LambdaExpression first, LambdaExpression second, 39 | Func merge) 40 | { 41 | // zip parameters (map from parameters of second to parameters of first) 42 | var map = first.Parameters 43 | .Select((f, i) => new { f, s = second.Parameters[i] }) 44 | .ToDictionary(p => p.s, p => p.f); 45 | 46 | // replace parameters in the second lambda expression with the parameters in the first 47 | var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); 48 | 49 | // create a merged lambda expression with parameters from the first expression 50 | return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); 51 | } 52 | 53 | private class ParameterRebinder : ExpressionVisitor 54 | { 55 | readonly Dictionary _map; 56 | 57 | private ParameterRebinder(Dictionary map) 58 | { 59 | _map = map ?? new Dictionary(); 60 | } 61 | 62 | public static Expression ReplaceParameters(Dictionary map, Expression exp) 63 | { 64 | return new ParameterRebinder(map).Visit(exp); 65 | } 66 | 67 | protected override Expression VisitParameter(ParameterExpression p) 68 | { 69 | ParameterExpression replacement; 70 | 71 | if (_map.TryGetValue(p, out replacement)) 72 | { 73 | p = replacement; 74 | } 75 | 76 | return base.VisitParameter(p); 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/Force/Extensions/EnumExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | 6 | namespace Force.Extensions 7 | { 8 | public static class EnumExtensions 9 | { 10 | public static string GetDisplayName(this Enum value) 11 | { 12 | var type = value.GetType(); 13 | var member = type.GetMember(value.ToString()); 14 | 15 | var displayName = (DisplayAttribute)member[0] 16 | .GetCustomAttributes(typeof(DisplayAttribute), false) 17 | .FirstOrDefault(); 18 | 19 | return displayName != null 20 | ? displayName.Name 21 | : SplitCamelCase(value.ToString()); 22 | } 23 | 24 | internal static string SplitCamelCase(string input) 25 | { 26 | return System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", " $1", 27 | System.Text.RegularExpressions.RegexOptions.Compiled).Trim(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/Force/Extensions/FunctionalExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.Threading.Tasks; 4 | 5 | namespace Force.Extensions 6 | { 7 | public static class FunctionalExtensions 8 | { 9 | public static void EnsureInvariant(this object obj, bool validateAllProperties = true) 10 | { 11 | Validator.ValidateObject(obj, new ValidationContext(obj), validateAllProperties); 12 | } 13 | 14 | public static TResult PipeTo( 15 | this TSource source, Func func) 16 | => func(source); 17 | 18 | public static async Task AwaitAndPipeTo( 19 | this Task source, Func func) 20 | => func(await source); 21 | 22 | 23 | public static TOutput EitherOr(this TInput o, Func ifTrue, 24 | Func ifFalse) 25 | => o.EitherOr(x => x != null, ifTrue, ifFalse); 26 | 27 | public static TOutput EitherOr(this TInput o, Func condition, 28 | Func ifTrue, Func ifFalse) 29 | => condition(o) ? ifTrue(o) : ifFalse(o); 30 | 31 | public static TOutput EitherOr(this TInput o, bool condition, 32 | Func ifTrue, Func ifFalse) 33 | => condition ? ifTrue(o) : ifFalse(o); 34 | } 35 | } -------------------------------------------------------------------------------- /src/Force/Force.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | True 5 | 2.0-alpha3 6 | General-purpose .NET Library for rapid application development 7 | https://github.com/hightechgroup/force 8 | HighTech Group 9 | HighTech Group 10 | https://github.com/hightechgroup/force 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/Force/Linq/ComposeKind.cs: -------------------------------------------------------------------------------- 1 | namespace Force.Linq 2 | { 3 | public enum ComposeKind 4 | { 5 | And, Or 6 | } 7 | } -------------------------------------------------------------------------------- /src/Force/Linq/Conventions/EnumerableConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace Force.Linq.Conventions 8 | { 9 | public class EnumerableConvention : IFilterConvention 10 | { 11 | private static MethodInfo Contains = typeof(Enumerable) 12 | .GetMethods() 13 | .First(x => x.Name == "Contains" && x.GetParameters().Length == 2); 14 | 15 | public bool CanConvert(Type predicateType, Type targetType) => 16 | targetType.GetElementType() == predicateType; 17 | 18 | public Expression BuildFilterBody(MemberExpression propertyExpression, Expression valueExpression) 19 | { 20 | return Expression.Call(null, Contains.MakeGenericMethod(propertyExpression.Type), valueExpression, propertyExpression); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/Force/Linq/Conventions/FilterConventionBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Force.Linq.Conventions 5 | { 6 | public abstract class FilterConventionBase: IFilterConvention 7 | { 8 | public bool CanConvert(Type predicateType, Type targetType) 9 | => predicateType == typeof(T) && targetType == typeof(T); 10 | 11 | public abstract Expression BuildFilterBody(MemberExpression propertyExpression, Expression valueExpression); 12 | } 13 | } -------------------------------------------------------------------------------- /src/Force/Linq/Conventions/FilterConventions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Force.Linq.Conventions 6 | { 7 | public class FilterConventions 8 | { 9 | private FilterConventions(IEnumerable filterConventions = null) 10 | { 11 | _filterConventions.AddRange(filterConventions ?? DefaultConventions); 12 | } 13 | 14 | public static readonly IEnumerable DefaultConventions = 15 | new[] 16 | { 17 | new StringConvention() as IFilterConvention, 18 | new EnumerableConvention() 19 | }; 20 | 21 | public static FilterConventions InitializeWithDefaultConventions() 22 | => Initialize(DefaultConventions); 23 | 24 | public static FilterConventions Initialize(IEnumerable conventions = null) 25 | { 26 | if (_instance != null) 27 | { 28 | throw new InvalidOperationException("Filter conventions are already initialized"); 29 | } 30 | 31 | _instance = new FilterConventions(conventions); 32 | return Instance; 33 | } 34 | 35 | private static FilterConventions _instance; 36 | 37 | public static FilterConventions Instance 38 | { 39 | get 40 | { 41 | if (_instance == null) 42 | { 43 | InitializeWithDefaultConventions(); 44 | } 45 | 46 | return _instance; 47 | } 48 | } 49 | 50 | private List _filterConventions = new List(); 51 | 52 | internal IFilterConvention GetConvention(Type targetType, Type valueType) => 53 | _filterConventions 54 | .FirstOrDefault(x => 55 | x.CanConvert(targetType, valueType)); 56 | } 57 | } -------------------------------------------------------------------------------- /src/Force/Linq/Conventions/IFilterConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Force.Linq.Conventions 5 | { 6 | public interface IFilterConvention 7 | { 8 | bool CanConvert(Type predicateType, Type targetType); 9 | 10 | Expression BuildFilterBody(MemberExpression propertyExpression, Expression valueExpression); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Force/Linq/Conventions/StringConvention.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Force.Linq.Conventions 6 | { 7 | public class StringConvention: FilterConventionBase 8 | { 9 | private static MethodInfo ToLower = typeof(string) 10 | .GetMethod("ToLower", new Type[]{}); 11 | 12 | private static MethodInfo Contains = typeof(string) 13 | .GetMethod("Contains", new[] {typeof(string)}); 14 | 15 | private static MethodInfo StartsWith = typeof(string) 16 | .GetMethod("StartsWith", new[] {typeof(string)}); 17 | 18 | public override Expression BuildFilterBody(MemberExpression propertyExpression, Expression valueExpression) 19 | { 20 | return Expression.Call(Expression.Call(propertyExpression, ToLower), 21 | propertyExpression.Member.GetCustomAttribute() 22 | ?.SearchKind == SearchKind.Contains 23 | ? Contains 24 | : StartsWith, 25 | valueExpression); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Force/Linq/IFilter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Force.Ddd; 3 | 4 | namespace Force.Linq 5 | { 6 | public interface IFilter 7 | { 8 | IQueryable Filter(IQueryable queryable); 9 | } 10 | } -------------------------------------------------------------------------------- /src/Force/Linq/ISorter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Force.Linq 4 | { 5 | public interface ISorter 6 | { 7 | IOrderedQueryable Sort(IQueryable queryable); 8 | } 9 | } -------------------------------------------------------------------------------- /src/Force/Linq/Pagination/IPaging.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Force.Linq.Pagination 4 | { 5 | public interface IPaging 6 | { 7 | int Page { get; } 8 | 9 | int Take { get; } 10 | } 11 | 12 | public static class PagingExtensions 13 | { 14 | public static IOrderedQueryable Paginate(this IOrderedQueryable queryable, IPaging paging) 15 | => (IOrderedQueryable)queryable 16 | .Skip((paging.Page - 1) * paging.Take) 17 | .Take(paging.Take); 18 | 19 | public static PagedEnumerable ToPagedEnumerable(this IOrderedQueryable queryable, IPaging paging) 20 | => new PagedEnumerable(queryable, paging); 21 | } 22 | } -------------------------------------------------------------------------------- /src/Force/Linq/Pagination/PagedEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Force.Linq.Pagination 6 | { 7 | public class PagedEnumerable: IEnumerable 8 | { 9 | public IEnumerable Items { get; protected set; } 10 | 11 | public long Total { get; protected set;} 12 | 13 | public PagedEnumerable(IEnumerable items, long total) 14 | { 15 | Total = total; 16 | Items = items; 17 | } 18 | 19 | public IEnumerator GetEnumerator() => Items.GetEnumerator(); 20 | } 21 | public class PagedEnumerable: PagedEnumerable, IEnumerable 22 | { 23 | private IEnumerable _items; 24 | 25 | public new IEnumerable Items => _items == null 26 | ? (_items = base.Items.Cast().ToArray()) 27 | : _items; 28 | 29 | public PagedEnumerable(IOrderedQueryable queryable, IPaging paging) 30 | : base(queryable.Paginate(paging).ToList(), queryable.Count()) 31 | { 32 | } 33 | 34 | public PagedEnumerable(IEnumerable items, long total) 35 | // ReSharper disable once PossibleMultipleEnumeration 36 | : base(items, total) 37 | { 38 | // ReSharper disable once PossibleMultipleEnumeration 39 | _items = items; 40 | } 41 | 42 | public new IEnumerator GetEnumerator() 43 | => Items.GetEnumerator(); 44 | } 45 | } -------------------------------------------------------------------------------- /src/Force/Linq/Pagination/Paging.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace Force.Linq.Pagination 5 | { 6 | public class Paging : IPaging 7 | { 8 | public Paging(int page, int take) 9 | { 10 | Page = page; 11 | Take = take; 12 | } 13 | 14 | public Paging() 15 | { 16 | Page = DefaultStartPage; 17 | Take = DefaultTake; 18 | } 19 | 20 | private int _page; 21 | private int _take; 22 | 23 | public const int MaxTake = 1000; 24 | 25 | public static int DefaultStartPage = 1; 26 | 27 | public static int DefaultTake = 30; 28 | 29 | public int Page 30 | { 31 | get => _page; 32 | set 33 | { 34 | if (value <= 0) 35 | { 36 | throw new ArgumentException("Page must be > 0", nameof(value)); 37 | } 38 | 39 | _page = value; 40 | } 41 | } 42 | 43 | [Range(1, MaxTake)] 44 | public virtual int Take 45 | { 46 | get => _take; 47 | set 48 | { 49 | if (value <= 0) 50 | { 51 | throw new ArgumentException("take must be > 0", nameof(value)); 52 | } 53 | 54 | _take = value; 55 | } 56 | } 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Force/Linq/QueryableExtentions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Force.Ddd; 6 | using Force.Linq.Pagination; 7 | 8 | namespace Force.Linq 9 | { 10 | public static class QueryableExtentions 11 | { 12 | #region Filter 13 | 14 | public static IQueryable FilterByConventions(this IQueryable queryable, object filter, 15 | ComposeKind composeKind = ComposeKind.And) 16 | { 17 | if (filter == null) throw new ArgumentNullException(nameof(filter)); 18 | var spec = (Spec)SpecBuilder.Build((dynamic) filter, composeKind); 19 | return spec != null 20 | ? queryable.Where(spec) 21 | : queryable; 22 | } 23 | 24 | public static IQueryable Filter(this IQueryable queryable, IFilter filter) 25 | => filter.Filter(queryable); 26 | 27 | public static IOrderedQueryable FilterAndSort(this IQueryable queryable, TQuery query) 28 | where TQuery : IFilter, ISorter 29 | => queryable 30 | .Filter(query) 31 | .Sort(query); 32 | 33 | public static PagedEnumerable FilterSortAndPaginate(this IQueryable queryable, TQuery query) 34 | where TQuery : IFilter, ISorter, IPaging 35 | => queryable 36 | .FilterAndSort(query) 37 | .ToPagedEnumerable(query); 38 | 39 | public static IQueryable WhereIf(this IQueryable query, bool condition, 40 | Expression> predicate) 41 | { 42 | return condition ? query.Where(predicate) : query; 43 | } 44 | 45 | public static IQueryable WhereIf(this IQueryable query, bool condition, 46 | Expression> predicateIfTrue, Expression> predicateIfFalse) 47 | => condition 48 | ? query.Where(predicateIfTrue) 49 | : query.Where(predicateIfFalse); 50 | 51 | public static IQueryable WhereIfNotNull(this IQueryable query, object obj, 52 | Expression> predicateIfTrue) 53 | => query.WhereIf(obj != null, predicateIfTrue); 54 | 55 | #endregion 56 | 57 | #region Order 58 | 59 | public static IOrderedQueryable OrderById(this IQueryable queryable) 60 | where T : IHasId 61 | => queryable.OrderBy(x => x.Id); 62 | 63 | public static IOrderedQueryable OrderBy(this IQueryable source, string orderPropertyName) 64 | { 65 | try 66 | { 67 | return source.OrderBy(ToLambda(orderPropertyName)); 68 | } 69 | catch (ArgumentException e) when(e.Message.Contains("Instance property") 70 | && e.Message.Contains("is not defined for type")) 71 | { 72 | throw new ArgumentException( 73 | $"Order property '{orderPropertyName}' is not defined for type {typeof(T)}", 74 | nameof(orderPropertyName), e); 75 | } 76 | 77 | } 78 | 79 | public static IOrderedQueryable OrderByDescending(this IQueryable source, string orderPropertyName) 80 | { 81 | try 82 | { 83 | return source.OrderByDescending(ToLambda(orderPropertyName)); 84 | } 85 | catch (ArgumentException e) when (e.Message.Contains("Instance property") 86 | && e.Message.Contains("is not defined for type")) 87 | { 88 | throw new ArgumentException( 89 | $"Order property '{orderPropertyName}' is not defined for type {typeof(T)}", 90 | nameof(orderPropertyName), e); 91 | } 92 | } 93 | 94 | private static Expression> ToLambda(string propertyName) 95 | { 96 | var parameter = Expression.Parameter(typeof(T)); 97 | var property = Expression.Property(parameter, propertyName); 98 | var propAsObject = Expression.Convert(property, typeof(object)); 99 | 100 | return Expression.Lambda>(propAsObject, parameter); 101 | } 102 | #endregion 103 | 104 | #region ById 105 | 106 | public static IQueryable ById(this IQueryable queryable, TKey id) 107 | where TKey : IEquatable 108 | where TEntity : class, IHasId 109 | => queryable.Where(x => x.Id.Equals(id)); 110 | 111 | public static TEntity FirstOrDefaultById(this IQueryable queryable, TKey id) 112 | where TKey : IEquatable 113 | where TEntity : class, IHasId 114 | => queryable.FirstOrDefault(x => x.Id.Equals(id)); 115 | 116 | public static TProjection FirstOrDefaultById(this IQueryable queryable, 117 | TKey id, Expression> projectionExpression) 118 | where TKey : IEquatable 119 | where TEntity : class, IHasId 120 | where TProjection : class, IHasId 121 | => queryable 122 | .Select(projectionExpression) 123 | .FirstOrDefault(x => x.Id.Equals(id)); 124 | 125 | #endregion 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Force/Linq/SearchByAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Linq 4 | { 5 | public enum SearchKind 6 | { 7 | StartsWith, 8 | Contains 9 | } 10 | 11 | public class SearchByAttribute : Attribute 12 | { 13 | public SearchKind SearchKind { get; } 14 | 15 | public SearchByAttribute(SearchKind searchKind = SearchKind.StartsWith) 16 | { 17 | SearchKind = searchKind; 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /src/Force/Linq/Sorter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Force.Reflection; 6 | 7 | namespace Force.Linq 8 | { 9 | public class Sorter: ISorter 10 | { 11 | private readonly string _propertyName; 12 | 13 | private bool _asc; 14 | 15 | public bool IsAsc => _asc; 16 | 17 | public static bool TryParse(string str, out Sorter sorter) 18 | { 19 | (string, bool) GetSorting(string prop) 20 | { 21 | prop = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(prop); 22 | var arr = prop.Split(new[]{".", " "}, StringSplitOptions.RemoveEmptyEntries); 23 | if (arr.Length == 1) 24 | return (arr[0], true); 25 | var sort = arr[1]; 26 | 27 | if (string.Equals(sort, "ASC", StringComparison.InvariantCulture)) 28 | { 29 | return (arr[0], true); 30 | } 31 | 32 | if (string.Equals(sort, "DESC", StringComparison.InvariantCulture)) 33 | { 34 | return (arr[0], false); 35 | } 36 | 37 | return ("", true); 38 | } 39 | 40 | var (name, asc) = GetSorting(str); 41 | 42 | if (!Type.PublicProperties.ContainsKey(name)) 43 | { 44 | sorter = null; 45 | return false; 46 | } 47 | 48 | sorter = new Sorter(name, asc); 49 | return true; 50 | } 51 | 52 | public Sorter(string propertyName, bool asc = true) 53 | { 54 | _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); 55 | if (!Type.PublicProperties.ContainsKey(_propertyName)) 56 | { 57 | throw new ArgumentException(propertyName, 58 | $"Property \"{_propertyName}\" doesn't exist in type \"{typeof(T)}\""); 59 | } 60 | 61 | _asc = asc; 62 | } 63 | 64 | public IOrderedQueryable Sort(IQueryable queryable) 65 | => _asc 66 | ? queryable.OrderBy(_propertyName) 67 | : queryable.OrderByDescending(_propertyName); 68 | } 69 | 70 | public class Sorter: ISorter 71 | { 72 | private readonly Expression> _expression; 73 | private readonly bool _asc; 74 | 75 | public Sorter(Expression> expression, bool asc = true) 76 | { 77 | _expression = expression ?? throw new ArgumentNullException(nameof(expression)); 78 | _asc = asc; 79 | } 80 | 81 | public static implicit operator Expression>(Sorter sorter) 82 | => sorter._expression; 83 | 84 | public IOrderedQueryable Sort(IQueryable queryable) 85 | => _asc 86 | ? queryable.OrderBy(_expression) 87 | : queryable.OrderByDescending(_expression); 88 | } 89 | 90 | public static class SorterExtensions 91 | { 92 | public static IOrderedQueryable Sort(this IQueryable queryable, ISorter sorter) 93 | => sorter.Sort(queryable); 94 | } 95 | } -------------------------------------------------------------------------------- /src/Force/Reflection/Type.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | 8 | namespace Force.Reflection 9 | { 10 | public delegate T ObjectActivator(params object[] args); 11 | 12 | /// 13 | /// Type helper 14 | /// 15 | /// 16 | public static class Type 17 | { 18 | private static Attribute[] _attributes; 19 | 20 | private static Dictionary _properties; 21 | 22 | private static MethodInfo[] _methods; 23 | 24 | private static ConstructorInfo[] _constructors; 25 | 26 | private static ConcurrentDictionary> _activators; 27 | 28 | static Type() 29 | { 30 | var type = typeof(T); 31 | _attributes = type.GetCustomAttributes().ToArray(); 32 | 33 | _properties = type 34 | .GetProperties() 35 | //.Where(x => x.CanRead && x.CanWrite) 36 | .ToDictionary(x => x.Name, x => x); 37 | 38 | _methods = type 39 | .GetMethods() 40 | .Where(x => x.IsPublic && !x.IsAbstract) 41 | .ToArray(); 42 | 43 | _constructors = typeof(T).GetConstructors(); 44 | _activators = new ConcurrentDictionary>(); 45 | } 46 | 47 | public static Dictionary PublicProperties => _properties; 48 | 49 | public static MethodInfo[] PublicMethods => _methods; 50 | 51 | public static Attribute[] Attributes => _attributes; 52 | 53 | public static bool HasAttribute() 54 | where TAttr : Attribute 55 | => Attributes.Any(x => x.GetType() == typeof(TAttr)); 56 | 57 | public static TAttr GetCustomAttribute() 58 | where TAttr: Attribute 59 | => (TAttr)_attributes.FirstOrDefault(x => x.GetType() == typeof(TAttr)); 60 | 61 | #region Create 62 | 63 | public static T CreateInstance(params object[] args) 64 | => _activators.GetOrAdd( 65 | GetSignature(args), 66 | x => GetActivator(GetConstructorInfo(args))) 67 | .Invoke(args); 68 | 69 | internal static long GetSignature(object[] args) 70 | { 71 | long hc = 0; 72 | unchecked 73 | { 74 | // ReSharper disable once LoopCanBeConvertedToQuery 75 | foreach (var arg in args) 76 | { 77 | hc = hc* 23 + (arg?.GetHashCode() ?? 0); 78 | } 79 | } 80 | 81 | return hc; 82 | } 83 | 84 | public static ConstructorInfo GetConstructorInfo(object[] args) 85 | { 86 | for (var i = 0; i < _constructors.Length; i++) 87 | { 88 | var constructor = _constructors[i]; 89 | var ctrParams = constructor.GetParameters(); 90 | if (ctrParams.Length != args.Length) 91 | { 92 | continue; 93 | } 94 | 95 | var flag = true; 96 | for (var j = 0; j < args.Length; j++) 97 | { 98 | if (ctrParams[j].ParameterType != args[j].GetType()) 99 | { 100 | flag = false; 101 | break; 102 | } 103 | } 104 | 105 | if (!flag) 106 | { 107 | continue; 108 | } 109 | 110 | return constructor; 111 | } 112 | 113 | var signature = GetSignature(args); 114 | 115 | throw new InvalidOperationException( 116 | $"Constructor ({signature}) is not found for {typeof(T)}"); 117 | } 118 | 119 | public static ObjectActivator GetActivator(ConstructorInfo ctor) 120 | { 121 | var type = ctor.DeclaringType; 122 | var paramsInfo = ctor.GetParameters(); 123 | 124 | //create a single param of type object[] 125 | var param = Expression.Parameter(typeof(object[]), "args"); 126 | 127 | var argsExp = new Expression[paramsInfo.Length]; 128 | 129 | //pick each arg from the params array 130 | //and create a typed expression of them 131 | for (var i = 0; i < paramsInfo.Length; i++) 132 | { 133 | var index = Expression.Constant(i); 134 | var paramType = paramsInfo[i].ParameterType; 135 | 136 | Expression paramAccessorExp = Expression.ArrayIndex(param, index); 137 | Expression paramCastExp = Expression.Convert (paramAccessorExp, paramType); 138 | 139 | argsExp[i] = paramCastExp; 140 | } 141 | 142 | //make a NewExpression that calls the 143 | //ctor with the args we just created 144 | var newExp = Expression.New(ctor,argsExp); 145 | 146 | //create a lambda with the New 147 | //Expression as body and our param object[] as arg 148 | var lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param); 149 | 150 | //compile it 151 | var compiled = (ObjectActivator)lambda.Compile(); 152 | return compiled; 153 | } 154 | 155 | #endregion 156 | 157 | public static Expression> PropertyGetter(string propertyName) 158 | { 159 | var paramExpression = Expression.Parameter(typeof(T), "value"); 160 | var propertyGetterExpression = Expression.Property(paramExpression, propertyName); 161 | return Expression 162 | .Lambda>(propertyGetterExpression, paramExpression); 163 | } 164 | 165 | public static Expression> PropertySetter(string propertyName) 166 | { 167 | var paramExpression = Expression.Parameter(typeof(T)); 168 | var paramExpression2 = Expression.Parameter(typeof(TProperty), propertyName); 169 | var propertyGetterExpression = Expression.Property(paramExpression, propertyName); 170 | return Expression.Lambda> 171 | ( 172 | Expression.Assign(propertyGetterExpression, paramExpression2), 173 | paramExpression, paramExpression2 174 | ); 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/Force/Reflection/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.Reflection 4 | { 5 | public static class TypeExtensions 6 | { 7 | public static bool TryGetValue(this object obj, string propertyName, out object value) 8 | { 9 | value = obj 10 | ?.GetType() 11 | .GetProperty(propertyName) 12 | ?.GetValue(obj); 13 | 14 | return value != default; 15 | } 16 | } 17 | } --------------------------------------------------------------------------------