{"id":28075384,"url":"https://github.com/ubf21/vali-flow","last_synced_at":"2026-06-20T06:31:53.107Z","repository":{"id":276170614,"uuid":"928443848","full_name":"UBF21/Vali-Flow","owner":"UBF21","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-12T16:37:10.000Z","size":1274,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-13T10:23:17.240Z","etag":null,"topics":["csharp","elasticsearch","entity-framework","fluent-api","linq","mongodb","sql"],"latest_commit_sha":null,"homepage":"https://vali-flow-docs.netlify.app/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/UBF21.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-06T16:45:39.000Z","updated_at":"2026-05-12T16:59:34.000Z","dependencies_parsed_at":"2025-04-09T01:23:02.032Z","dependency_job_id":"8fcc9b4c-5c7d-4dfc-8c5b-9b6a32b894f4","html_url":"https://github.com/UBF21/Vali-Flow","commit_stats":null,"previous_names":["ubf21/vali-flow"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/UBF21/Vali-Flow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UBF21%2FVali-Flow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UBF21%2FVali-Flow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UBF21%2FVali-Flow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UBF21%2FVali-Flow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/UBF21","download_url":"https://codeload.github.com/UBF21/Vali-Flow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UBF21%2FVali-Flow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34536283,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","elasticsearch","entity-framework","fluent-api","linq","mongodb","sql"],"created_at":"2025-05-13T00:56:48.295Z","updated_at":"2026-06-20T06:31:53.095Z","avatar_url":"https://github.com/UBF21.png","language":"C#","funding_links":["https://paypal.me/felipeRMM?country.x=PE\u0026locale.x=es_XC"],"categories":[],"sub_categories":[],"readme":"# Vali-Flow\n\n## Overview\n\nVali-Flow is a comprehensive .NET library ecosystem for building reusable, composable query criteria with a fluent API. It enables you to define filter logic once using a simple, expression-based DSL and translate it into:\n\n- **Entity Framework Core** queries (async)\n- **Parameterized SQL** for Dapper / ADO.NET (SQL Server, PostgreSQL, MySQL, SQLite)\n- **MongoDB BSON** filters\n- **Elasticsearch Query DSL**\n- **Redis (RediSearch)** queries\n- **AWS DynamoDB** filter expressions\n- **In-memory** evaluation (LINQ-to-Objects)\n\nAll built on **Vali-Flow.Core** — a lightweight expression builder with zero additional dependencies.\n\n**Supported platforms:** .NET 8.0, .NET 9.0\n\n---\n\n## The Problem It Solves\n\nWhen working with multiple data stores or ORM patterns, you typically scatter filter logic across repositories, duplicate predicates per store, or couple business logic to data access code:\n\n```csharp\n// ❌ Traditional approach: filter logic is scattered\npublic async Task\u003cList\u003cOrder\u003e\u003e GetActiveOrdersEF(DbContext db, decimal minTotal)\n{\n    return await db.Orders\n        .Where(o =\u003e o.Status == \"Active\" \u0026\u0026 o.Total \u003e minTotal)\n        .ToListAsync();\n}\n\npublic List\u003cOrder\u003e GetActiveOrdersMongo(IMongoCollection\u003cOrder\u003e coll, decimal minTotal)\n{\n    return coll.Find(Builders\u003cOrder\u003e.Filter.And(\n        Builders\u003cOrder\u003e.Filter.Eq(o =\u003e o.Status, \"Active\"),\n        Builders\u003cOrder\u003e.Filter.Gt(o =\u003e o.Total, minTotal)\n    )).ToList();\n}\n\npublic DataTable GetActiveOrdersSQL(SqlConnection conn, decimal minTotal)\n{\n    var cmd = new SqlCommand(\n        \"SELECT * FROM Orders WHERE Status = @status AND Total \u003e @total\", conn);\n    cmd.Parameters.AddWithValue(\"@status\", \"Active\");\n    cmd.Parameters.AddWithValue(\"@total\", minTotal);\n    // ...\n}\n```\n\n**With Vali-Flow:**\n\n```csharp\n// ✅ Single filter definition\nvar filter = new ValiFlow\u003cOrder\u003e()\n    .EqualTo(x =\u003e x.Status, \"Active\")\n    .GreaterThan(x =\u003e x.Total, minTotal);\n\n// Use the same filter everywhere\nvar efOrders = await new ValiFlowEvaluator\u003cOrder\u003e(dbContext)\n    .EvaluateQueryAsync(new BasicSpecification\u003cOrder\u003e().WithFilter(filter));\n\nvar mongoOrders = mongoCollection.Find(filter.ToMongo()).ToList();\n\nvar sqlResult = filter.ToSql(new SqlServerDialect());\nvar sqlOrders = await connection.QueryAsync\u003cOrder\u003e(\n    $\"SELECT * FROM Orders WHERE {sqlResult.Sql}\",\n    sqlResult.Parameters);\n```\n\n---\n\n## Package Ecosystem\n\n### Core Package\n\n| Package | Purpose | Version |\n|---------|---------|---------|\n| **Vali-Flow.Core** | Fluent expression builder (`ValiFlow\u003cT\u003e`) - shared by all packages | [2.0.0](https://www.nuget.org/packages/Vali-Flow.Core) |\n\n### Data Access Packages\n\n| Package | Purpose | Target | Version |\n|---------|---------|--------|---------|\n| **Vali-Flow** | EF Core async evaluator + specifications (read/write) | `DbContext` | [1.1.0](https://www.nuget.org/packages/Vali-Flow) |\n| **Vali-Flow.InMemory** | Synchronous in-memory evaluator for testing \u0026 caching | `IEnumerable\u003cT\u003e` | [1.0.0](https://www.nuget.org/packages/Vali-Flow.InMemory) |\n| **Vali-Flow.Sql** | SQL query builder for parameterized queries | Dapper / ADO.NET | [1.0.0](https://www.nuget.org/packages/Vali-Flow.Sql) |\n\n### NoSQL Packages\n\n| Package | Database | Output Type | Version |\n|---------|----------|-------------|---------|\n| **Vali-Flow.NoSql.MongoDB** | MongoDB | `BsonDocument` | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.MongoDB) |\n| **Vali-Flow.NoSql.Elasticsearch** | Elasticsearch | `Query` (Elastic.Clients) | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Elasticsearch) |\n| **Vali-Flow.NoSql.Redis** | Redis (RediSearch) | Query string | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.Redis) |\n| **Vali-Flow.NoSql.DynamoDB** | AWS DynamoDB | `DynamoFilterExpression` | [1.0.0](https://www.nuget.org/packages/Vali-Flow.NoSql.DynamoDB) |\n\n### Architecture\n\n```\nVali-Flow.Core  (expression builder — ValiFlow\u003cT\u003e)\n       │\n       ├─── Vali-Flow                    (EF Core async)\n       ├─── Vali-Flow.InMemory           (sync in-memory)\n       ├─── Vali-Flow.Sql                (SQL: SQL Server, PostgreSQL, MySQL, SQLite)\n       │\n       └─── Vali-Flow.NoSql\n               ├─── Vali-Flow.NoSql.MongoDB        (MongoDB BSON)\n               ├─── Vali-Flow.NoSql.Elasticsearch  (Elasticsearch Query DSL)\n               ├─── Vali-Flow.NoSql.Redis          (RediSearch)\n               └─── Vali-Flow.NoSql.DynamoDB       (DynamoDB filter expressions)\n```\n\n---\n\n## Quick Start\n\n### 1. Define a filter once\n\n```csharp\nusing Vali_Flow.Core;\n\nvar filter = new ValiFlow\u003cOrder\u003e()\n    .EqualTo(x =\u003e x.Status, \"Active\")\n    .GreaterThan(x =\u003e x.Total, 100m)\n    .IsAfter(x =\u003e x.CreatedAt, DateTime.UtcNow.AddDays(-30));\n```\n\n### 2. Use it with EF Core\n\n```csharp\nusing Vali_Flow;\n\nvar evaluator = new ValiFlowEvaluator\u003cOrder\u003e(dbContext);\nvar spec = new BasicSpecification\u003cOrder\u003e()\n    .WithFilter(filter)\n    .WithAsNoTracking(true)\n    .AddInclude(x =\u003e x.Customer);\n\nvar orders = await evaluator.EvaluateQueryAsync(spec, cancellationToken);\n```\n\n### 3. Or with SQL/Dapper\n\n```csharp\nusing Vali_Flow.Sql.Extensions;\nusing Vali_Flow.Sql.Dialects;\n\nvar result = filter.ToSql(new PostgreSqlDialect());\n\nvar orders = await connection.QueryAsync\u003cOrder\u003e(\n    $\"SELECT * FROM orders WHERE {result.Sql}\",\n    result.Parameters);\n```\n\n### 4. Or with MongoDB\n\n```csharp\nusing Vali_Flow.NoSql.MongoDB.Extensions;\n\nvar bsonFilter = filter.ToMongo();\nvar orders = await collection.Find(bsonFilter).ToListAsync();\n```\n\n### 5. Or in-memory (testing)\n\n```csharp\nusing Vali_Flow.InMemory;\n\nvar evaluator = new ValiFlowEvaluator\u003cOrder, int\u003e(orders, null, x =\u003e x.Id);\nvar filtered = evaluator.EvaluateAll\u003cDateTime\u003e(\n    orders,\n    orderBy: x =\u003e x.CreatedAt,\n    valiFlow: filter);\n```\n\n---\n\n## Installation\n\n**Install the package(s) you need:**\n\n```bash\n# EF Core (production)\ndotnet add package Vali-Flow\n\n# In-memory (testing / caching)\ndotnet add package Vali-Flow.InMemory\n\n# SQL queries (Dapper / ADO.NET)\ndotnet add package Vali-Flow.Sql\n\n# MongoDB\ndotnet add package Vali-Flow.NoSql.MongoDB\n\n# Elasticsearch\ndotnet add package Vali-Flow.NoSql.Elasticsearch\n\n# Redis (RediSearch)\ndotnet add package Vali-Flow.NoSql.Redis\n\n# AWS DynamoDB\ndotnet add package Vali-Flow.NoSql.DynamoDB\n```\n\nAll packages automatically include **Vali-Flow.Core** as a transitive dependency.\n\n---\n\n## Core Features\n\n### Fluent Filter DSL (`ValiFlow\u003cT\u003e`)\n\nBuild complex filters with a natural, chainable API:\n\n```csharp\nvar filter = new ValiFlow\u003cProduct\u003e()\n    // Comparison\n    .EqualTo(x =\u003e x.Category, \"Electronics\")\n    .GreaterThanOrEqualTo(x =\u003e x.Price, 100m)\n    // String operations\n    .Contains(x =\u003e x.Name, \"phone\")\n    .StartsWith(x =\u003e x.Sku, \"PROD\")\n    // Numeric ranges\n    .Between(x =\u003e x.Quantity, 1, 1000)\n    // Dates\n    .IsAfter(x =\u003e x.CreatedAt, DateTime.UtcNow.AddDays(-90))\n    // Collection\n    .NotEmpty(x =\u003e x.Reviews)\n    // Boolean\n    .IsTrue(x =\u003e x.IsActive)\n    // Logical operators\n    .Or()\n    .EqualTo(x =\u003e x.Category, \"Accessories\");\n```\n\nSee [Vali-Flow.Core](https://github.com/UBF21/vali-flow-core) for the full list of 50+ predicates.\n\n### Specifications\n\nEncapsulate query criteria, ordering, pagination, and eager loading:\n\n```csharp\nvar spec = new QuerySpecification\u003cOrder\u003e()\n    .WithFilter(filter)\n    .WithOrderBy(x =\u003e x.CreatedAt, ascending: false)\n    .AddThenBy(x =\u003e x.Total, ascending: true)\n    .WithPagination(page: 1, pageSize: 20)\n    .AddInclude(x =\u003e x.Customer)\n    .AddInclude(x =\u003e x.OrderLines)\n    .WithAsNoTracking(true);\n```\n\n### EF Core: Read Operations\n\n```csharp\nvar evaluator = new ValiFlowEvaluator\u003cOrder\u003e(dbContext);\n\n// Existence and count\nbool exists = await evaluator.EvaluateAnyAsync(spec);\nint count   = await evaluator.EvaluateCountAsync(spec);\n\n// Single entities\nOrder? first = await evaluator.EvaluateGetFirstAsync(spec);\nOrder? last  = await evaluator.EvaluateGetLastAsync(spec);\n\n// Full query\nIQueryable\u003cOrder\u003e query = await evaluator.EvaluateQueryAsync(spec);\n\n// Distinct and duplicates\nIQueryable\u003cOrder\u003e distinct   = await evaluator.EvaluateDistinctAsync(spec, x =\u003e x.CustomerId);\nIQueryable\u003cOrder\u003e duplicates = await evaluator.EvaluateDuplicatesAsync(spec, x =\u003e x.CustomerId);\n\n// Aggregates\ndecimal minTotal = await evaluator.EvaluateMinAsync(spec, x =\u003e x.Total);\ndecimal maxTotal = await evaluator.EvaluateMaxAsync(spec, x =\u003e x.Total);\ndecimal avgTotal = await evaluator.EvaluateAverageAsync(spec, x =\u003e x.Total);\ndecimal sumTotal = await evaluator.EvaluateSumAsync(spec, x =\u003e x.Total);\n\n// Grouped aggregates\nDictionary\u003cstring, int\u003e countByStatus = \n    await evaluator.EvaluateCountByGroupAsync(spec, x =\u003e x.Status);\n\nDictionary\u003cstring, decimal\u003e sumByStatus = \n    await evaluator.EvaluateSumByGroupAsync(spec, x =\u003e x.Status, x =\u003e x.Total);\n```\n\n### EF Core: Write Operations\n\n```csharp\nvar evaluator = new ValiFlowEvaluator\u003cOrder\u003e(dbContext);\n\n// Single entity\nvar added   = await evaluator.AddAsync(order, saveChanges: true);\nvar updated = await evaluator.UpdateAsync(order, saveChanges: true);\nawait evaluator.DeleteAsync(order, saveChanges: true);\n\n// Batch\nawait evaluator.AddRangeAsync(orders);\nawait evaluator.UpdateRangeAsync(orders);\nawait evaluator.DeleteRangeAsync(orders);\n\n// Conditional delete\nawait evaluator.DeleteByConditionAsync(\n    condition: x =\u003e x.Status == \"Expired\" \u0026\u0026 x.CreatedAt \u003c cutoffDate);\n\n// Upsert (insert if not found, update otherwise)\nvar upserted = await evaluator.UpsertAsync(\n    entity: order,\n    matchCondition: x =\u003e x.Id == order.Id);\n\n// Bulk operations (via EFCore.BulkExtensions)\nawait evaluator.BulkInsertAsync(orders, new BulkConfig { BatchSize = 5000 });\nawait evaluator.BulkUpdateAsync(orders, new BulkConfig { BatchSize = 5000 });\nawait evaluator.BulkInsertOrUpdateAsync(orders);\n\n// Transactions\nawait evaluator.ExecuteTransactionAsync(async () =\u003e\n{\n    await evaluator.AddAsync(order1, saveChanges: false);\n    await evaluator.UpdateAsync(order2, saveChanges: false);\n    await evaluator.SaveChangesAsync();\n});\n```\n\n### SQL Query Builder (Dapper / ADO.NET)\n\nFour dialects out of the box: SQL Server, PostgreSQL, MySQL, SQLite.\n\n```csharp\n// Simple WHERE clause\nvar result = filter.ToSql(new PostgreSqlDialect());\nvar orders = await connection.QueryAsync\u003cOrder\u003e(\n    $\"SELECT * FROM orders WHERE {result.Sql}\",\n    result.Parameters);\n\n// Full SELECT with JOIN, GROUP BY, aggregates\nvar query = new SqlQueryBuilder\u003cOrder\u003e(new SqlServerDialect())\n    .Select(x =\u003e x.Id, x =\u003e x.Status, x =\u003e x.Total)\n    .From(\"orders\")\n    .Where(w =\u003e w.EqualTo(x =\u003e x.Status, \"Active\"))\n    .OrderBy(x =\u003e x.CreatedAt, ascending: false)\n    .Paginate(page: 1, pageSize: 20);\n\nvar result = query.Build();\n```\n\n### In-Memory Evaluator (Testing / Caching)\n\nSynchronous, dependency-free evaluation against `IEnumerable\u003cT\u003e`:\n\n```csharp\nvar evaluator = new ValiFlowEvaluator\u003cOrder, int\u003e(orders, null, x =\u003e x.Id);\n\nvar filter = new ValiFlow\u003cOrder\u003e().EqualTo(x =\u003e x.Status, \"Active\");\n\nint count  = evaluator.EvaluateCount(orders, filter);\nOrder? first = evaluator.GetFirst(orders, filter);\n\nIEnumerable\u003cOrder\u003e filtered = evaluator.EvaluateAll\u003cDateTime\u003e(\n    orders,\n    orderBy: x =\u003e x.CreatedAt,\n    valiFlow: filter);\n\nDictionary\u003cstring, int\u003e countByStatus = \n    evaluator.EvaluateCountByGroup(orders, x =\u003e x.Status, filter);\n```\n\n---\n\n## NoSQL Support\n\n### MongoDB\n\n```csharp\nusing Vali_Flow.NoSql.MongoDB.Extensions;\n\nvar filter = new ValiFlow\u003cUser\u003e()\n    .EqualTo(x =\u003e x.IsActive, true)\n    .GreaterThan(x =\u003e x.Age, 18);\n\nBsonDocument bsonFilter = filter.ToMongo();\nvar users = await collection.Find(bsonFilter).ToListAsync();\n```\n\n### Elasticsearch\n\n```csharp\nusing Vali_Flow.NoSql.Elasticsearch.Extensions;\n\nvar filter = new ValiFlow\u003cProduct\u003e()\n    .EqualTo(x =\u003e x.Category, \"Electronics\")\n    .GreaterThanOrEqualTo(x =\u003e x.Price, 100m)\n    .Contains(x =\u003e x.Name, \"phone\");\n\nQuery esQuery = filter.ToElasticsearch();\nvar response = await client.SearchAsync\u003cProduct\u003e(s =\u003e s.Query(esQuery));\n```\n\n### Redis (RediSearch)\n\n```csharp\nusing Vali_Flow.NoSql.Redis.Extensions;\n\nstring redisQuery = filter.ToRedisSearch();\nvar results = db.FT().Search(\"idx:products\", new Query(redisQuery));\n```\n\n### DynamoDB\n\n```csharp\nusing Vali_Flow.NoSql.DynamoDB.Extensions;\n\nDynamoFilterExpression f = filter.ToDynamoDB();\n\nvar request = new ScanRequest\n{\n    TableName                 = \"Orders\",\n    FilterExpression          = f.FilterExpression,\n    ExpressionAttributeNames  = f.ExpressionAttributeNames.ToDictionary(),\n    ExpressionAttributeValues = f.ExpressionAttributeValues.ToDictionary()\n};\n```\n\n---\n\n## Documentation\n\n- **[Full Feature Guide](docs/FEATURES.md)** — Detailed examples for each package\n- **[Architecture Guide](docs/ARCHITECTURE.md)** — Design patterns and decision rationale\n- **[SQL Dialects Reference](Vali-Flow.Sql/README.md)** — SQL Builder capabilities\n- **[Vali-Flow.Core](https://github.com/UBF21/vali-flow-core)** — Expression builder predicates\n\n---\n\n## License\n\nLicensed under the [MIT License](LICENSE).  \nCopyright © 2025 Felipe Rafael Montenegro Morriberon. All rights reserved.\n\n---\n\n## Support\n\n- **Issues \u0026 Feature Requests:** [GitHub Issues](https://github.com/UBF21/vali-flow/issues)\n- **Discussions:** [GitHub Discussions](https://github.com/UBF21/vali-flow/discussions)\n\n### Contribute\n\nContributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\nIf this project helps you, consider supporting its development:\n- **Latin America** — [MercadoPago](https://link.mercadopago.com.pe/felipermm)\n- **International** — [PayPal](https://paypal.me/felipeRMM?country.x=PE\u0026locale.x=es_XC)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fubf21%2Fvali-flow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fubf21%2Fvali-flow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fubf21%2Fvali-flow/lists"}