![]() |
VOOZH | about |
dotnet add package Ecng.Linq --version 1.0.176
NuGet\Install-Package Ecng.Linq -Version 1.0.176
<PackageReference Include="Ecng.Linq" Version="1.0.176" />
<PackageVersion Include="Ecng.Linq" Version="1.0.176" />Directory.Packages.props
<PackageReference Include="Ecng.Linq" />Project file
paket add Ecng.Linq --version 1.0.176
#r "nuget: Ecng.Linq, 1.0.176"
#:package Ecng.Linq@1.0.176
#addin nuget:?package=Ecng.Linq&version=1.0.176Install as a Cake Addin
#tool nuget:?package=Ecng.Linq&version=1.0.176Install as a Cake Tool
A comprehensive library providing powerful extensions for LINQ, IQueryable, IAsyncEnumerable, and expression tree manipulation in .NET.
Ecng.Linq extends the standard LINQ capabilities with:
Add a reference to the Ecng.Linq project in your .csproj file:
<ItemGroup>
<ProjectReference Include="path\to\Ecng.Linq\Linq.csproj" />
</ItemGroup>
Order queryable collections dynamically using property names as strings, perfect for scenarios where sort order is determined at runtime (e.g., user input, configuration).
using Ecng.Linq;
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public DateTime CreatedDate { get; set; }
}
IQueryable<Product> products = dbContext.Products;
// Order by property name (case-sensitive)
var orderedByName = products.OrderBy("Name", ignoreCase: false);
// Order by property name (case-insensitive)
var orderedByPrice = products.OrderBy("price", ignoreCase: true);
// Nested property ordering
var orderedByNested = products.OrderBy("Category.Name", ignoreCase: false);
// Order descending by property name
var newestFirst = products.OrderByDescending("CreatedDate", ignoreCase: false);
// Case-insensitive descending order
var expensiveFirst = products.OrderByDescending("PRICE", ignoreCase: true);
// Multiple sort criteria
var sorted = products
.OrderBy("Category.Name", ignoreCase: false)
.ThenBy("Price", ignoreCase: false)
.ThenByDescending("CreatedDate", ignoreCase: false);
public IQueryable<Product> GetSortedProducts(string sortColumn, bool ascending)
{
IQueryable<Product> query = dbContext.Products;
if (ascending)
return query.OrderBy(sortColumn, ignoreCase: true);
else
return query.OrderByDescending(sortColumn, ignoreCase: true);
}
// Usage
var products = GetSortedProducts("Name", ascending: true);
Asynchronous operations for IQueryable sequences, ideal for database queries and remote data sources.
using Ecng.Linq;
using System.Threading;
IQueryable<Product> products = dbContext.Products.Where(p => p.Price > 100);
// Count elements asynchronously
long count = await products.CountAsync(CancellationToken.None);
Console.WriteLine($"Found {count} expensive products");
// Check if any elements exist
bool hasExpensiveProducts = await products.AnyAsync(CancellationToken.None);
if (hasExpensiveProducts)
{
Console.WriteLine("Expensive products are available");
}
// Get first element or default
Product firstExpensive = await products
.OrderBy("Price", ignoreCase: false)
.FirstOrDefaultAsync(CancellationToken.None);
if (firstExpensive != null)
{
Console.WriteLine($"Cheapest expensive product: {firstExpensive.Name}");
}
// Materialize query to array asynchronously
Product[] productArray = await products.ToArrayAsync(CancellationToken.None);
foreach (var product in productArray)
{
Console.WriteLine(product.Name);
}
// Skip a large number of elements (supports long instead of int)
var pagedResults = products
.OrderBy("CreatedDate", ignoreCase: false)
.SkipLong(10_000_000L)
.Take(100);
var results = await pagedResults.ToArrayAsync(CancellationToken.None);
Comprehensive LINQ-style operations for async sequences, providing the full power of LINQ for asynchronous data streams.
using Ecng.Linq;
using System.Collections.Generic;
using System.Threading;
async IAsyncEnumerable<int> GetNumbersAsync()
{
for (int i = 0; i < 100; i++)
{
await Task.Delay(10);
yield return i;
}
}
// Where - Filter elements
var evenNumbers = GetNumbersAsync().Where(n => n % 2 == 0);
// Select - Transform elements
var doubled = GetNumbersAsync().Select(n => n * 2);
// Take - Limit results
var first10 = GetNumbersAsync().Take(10);
// Skip - Skip elements
var skipFirst10 = GetNumbersAsync().Skip(10);
// Iterate asynchronously
await foreach (var number in first10)
{
Console.WriteLine(number);
}
IAsyncEnumerable<int> numbers = GetNumbersAsync();
// Count elements
int count = await numbers.CountAsync();
// Sum
int sum = await numbers.SumAsync();
// Average
double average = await numbers.AverageAsync();
// Min/Max
int min = await numbers.MinAsync();
int max = await numbers.MaxAsync();
IAsyncEnumerable<Product> asyncProducts = GetProductsAsync();
// First element
Product first = await asyncProducts.FirstAsync();
// First or default
Product firstOrNull = await asyncProducts.FirstOrDefaultAsync();
// Last element
Product last = await asyncProducts.LastAsync();
// Single element (throws if more than one)
Product single = await asyncProducts.SingleAsync();
// Element at index
Product atIndex = await asyncProducts.ElementAtAsync(5);
// Check if any elements exist
bool hasAny = await asyncProducts.AnyAsync();
// Check if any match predicate
bool hasExpensive = await asyncProducts.AnyAsync(p => p.Price > 1000);
// Check if all match predicate
bool allExpensive = await asyncProducts.AllAsync(p => p.Price > 100);
// Check if contains specific element
bool contains = await asyncProducts.ContainsAsync(specificProduct);
// To array
Product[] array = await asyncProducts.ToArrayAsync();
// To list
List<Product> list = await asyncProducts.ToListAsync();
// To dictionary
Dictionary<int, Product> dict = await asyncProducts
.ToDictionaryAsync(p => p.Id);
// To dictionary with value selector
Dictionary<int, string> nameDict = await asyncProducts
.ToDictionaryAsync(p => p.Id, p => p.Name);
// To hash set
HashSet<Product> set = await asyncProducts.ToHashSetAsync();
// Order by
var orderedProducts = asyncProducts.OrderBy(p => p.Price);
// Order by descending
var descProducts = asyncProducts.OrderByDescending(p => p.CreatedDate);
// Reverse
var reversed = asyncProducts.Reverse();
await foreach (var product in orderedProducts)
{
Console.WriteLine($"{product.Name}: ${product.Price}");
}
IAsyncEnumerable<int> sequence1 = GetSequence1();
IAsyncEnumerable<int> sequence2 = GetSequence2();
// Distinct elements
var distinct = sequence1.Distinct();
// Distinct by key
var distinctProducts = asyncProducts.DistinctBy(p => p.Name);
// Union
var union = sequence1.Union(sequence2);
// Intersect
var intersection = sequence1.Intersect(sequence2);
// Except (set difference)
var difference = sequence1.Except(sequence2);
IAsyncEnumerable<int> numbers = GetNumbersAsync();
// Concat - Combine sequences
var combined = numbers.Concat(GetMoreNumbersAsync());
// Append - Add element at end
var withExtra = numbers.Append(999);
// Prepend - Add element at start
var withPrefix = numbers.Prepend(-1);
// Chunk - Split into batches
var batches = numbers.Chunk(10);
await foreach (var batch in batches)
{
Console.WriteLine($"Batch of {batch.Length} items");
}
// Skip while condition is true
var skipLowPrices = asyncProducts.SkipWhile(p => p.Price < 100);
// Take while condition is true
var takeCheap = asyncProducts.TakeWhile(p => p.Price < 100);
// Default if empty
var withDefault = asyncProducts.DefaultIfEmpty();
// Default if empty with specific value
var withSpecificDefault = asyncProducts.DefaultIfEmpty(new Product { Name = "Default" });
// Select many - Flatten nested sequences
var allTags = asyncProducts.SelectMany(p => p.Tags);
// Zip - Combine two sequences
var prices = GetPricesAsync();
var names = GetNamesAsync();
var combined = prices.Zip(names, (price, name) => new { Name = name, Price = price });
// Zip with tuples
var tuples = prices.Zip(names);
await foreach (var (price, name) in tuples)
{
Console.WriteLine($"{name}: ${price}");
}
IAsyncEnumerable<object> mixedObjects = GetMixedObjectsAsync();
// Filter by type
IAsyncEnumerable<Product> onlyProducts = mixedObjects.OfType<Product>();
// Cast all elements
IAsyncEnumerable<Product> casted = mixedObjects.Cast<Product>();
// Cast with converter
var converted = asyncProducts.Cast<Product, ProductDto>(p => new ProductDto
{
Name = p.Name
});
// Group by key (assumes source is pre-sorted by key)
var grouped = asyncProducts.GroupByAsync(p => p.CategoryId);
await foreach (var group in grouped)
{
Console.WriteLine($"Category {group.Key}:");
foreach (var product in group)
{
Console.WriteLine($" - {product.Name}");
}
}
// Filter based on comparison with previous element
var changed = asyncPrices.WhereWithPrevious((prev, curr) => curr != prev);
await foreach (var price in changed)
{
Console.WriteLine($"Price changed to: {price}");
}
// Aggregate without seed
var concatenated = asyncProducts
.Select(p => p.Name)
.AggregateAsync((acc, name) => acc + ", " + name);
// Aggregate with seed
var total = await asyncProducts
.AggregateAsync(0m, (sum, product) => sum + product.Price);
Console.WriteLine($"Total value: ${total}");
IAsyncEnumerable<int> seq1 = GetSequence1Async();
IAsyncEnumerable<int> seq2 = GetSequence2Async();
// Check if sequences are equal
bool areEqual = await seq1.SequenceEqualAsync(seq2);
if (areEqual)
{
Console.WriteLine("Sequences are identical");
}
using System.Linq;
// Generate range asynchronously
var range = AsyncEnumerable.Range(0, 100);
// Repeat value asynchronously
var repeated = AsyncEnumerable.Repeat("Hello", 10);
// Empty sequence
var empty = AsyncEnumerable.Empty<Product>();
await foreach (var number in range)
{
Console.WriteLine(number);
}
Utilities for working with expression trees, useful for building dynamic queries and analyzing lambda expressions.
using Ecng.Linq;
using System.Linq.Expressions;
// Evaluate constant expression
Expression<Func<int>> expr = () => 42;
int result = expr.Body.Evaluate() as int?; // 42
// Evaluate complex expression
Expression<Func<int, int>> doubleExpr = x => x * 2;
// Note: Use Expression.Invoke for parameterized expressions
// Extract constant value from expression
ConstantExpression constExpr = Expression.Constant(100);
int value = constExpr.GetConstant<int>(); // 100
// Remove quote wrappers from expressions
Expression quotedExpr = Expression.Quote(Expression.Constant(42));
Expression unquoted = quotedExpr.StripQuotes();
using System.Reflection;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var person = new Person { Name = "John", Age = 30 };
PropertyInfo nameProperty = typeof(Person).GetProperty("Name");
// Get property value
object name = nameProperty.GetMemberValue(person); // "John"
// Extract value from member expression
Expression<Func<Person, string>> expr = p => p.Name;
var memberExpr = expr.Body as MemberExpression;
// Get the value if the expression can be evaluated
// string value = memberExpr.GetValue<string>();
public class Order
{
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
Expression<Func<Order, string>> expr = o => o.Customer.Name;
var memberExpr = expr.Body as MemberExpression;
// Get the innermost member
var innerMember = memberExpr.GetInnerMember(); // Points to 'o' parameter
using Ecng.Common;
// Convert expression type to comparison operator
ExpressionType greaterThan = ExpressionType.GreaterThan;
ComparisonOperator op = greaterThan.ToOperator(); // ComparisonOperator.Greater
// Supported conversions:
// GreaterThan -> Greater
// GreaterThanOrEqual -> GreaterOrEqual
// LessThan -> Less
// LessThanOrEqual -> LessOrEqual
// Equal -> Equal
// NotEqual -> NotEqual
// Replace the query provider in an expression tree
IQueryable<Product> query = dbContext.Products.Where(p => p.Price > 100);
IQueryProvider newProvider = customQueryProvider;
query.Expression.ReplaceSource(newProvider);
// The expression now uses the new provider
Convert between synchronous IEnumerable and asynchronous IAsyncEnumerable seamlessly.
using Ecng.Linq;
using System.Linq;
// From array
int[] numbers = { 1, 2, 3, 4, 5 };
IAsyncEnumerable<int> asyncNumbers = numbers.ToAsyncEnumerable();
// From list
List<Product> products = GetProducts();
IAsyncEnumerable<Product> asyncProducts = products.ToAsyncEnumerable();
// From LINQ query
var expensiveProducts = products
.Where(p => p.Price > 100)
.ToAsyncEnumerable();
// Iterate asynchronously
await foreach (var product in asyncProducts)
{
Console.WriteLine(product.Name);
}
// Manually wrap a synchronous enumerable
IEnumerable<int> syncNumbers = Enumerable.Range(1, 100);
var asyncWrapper = new SyncAsyncEnumerable<int>(syncNumbers);
await foreach (var number in asyncWrapper)
{
Console.WriteLine(number);
}
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5));
IAsyncEnumerable<int> numbers = syncNumbers.ToAsyncEnumerable();
try
{
await foreach (var number in numbers.WithCancellation(cancellationTokenSource.Token))
{
await Task.Delay(100);
Console.WriteLine(number);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation cancelled");
}
Here's a comprehensive example demonstrating multiple features:
using Ecng.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
public class ProductService
{
private readonly IQueryable<Product> _products;
public ProductService(IQueryable<Product> products)
{
_products = products;
}
// Dynamic sorting with async execution
public async Task<Product[]> GetSortedProductsAsync(
string sortBy,
bool ascending,
CancellationToken ct)
{
var query = ascending
? _products.OrderBy(sortBy, ignoreCase: true)
: _products.OrderByDescending(sortBy, ignoreCase: true);
return await query.ToArrayAsync(ct);
}
// Pagination with large datasets
public async Task<Product[]> GetPagedProductsAsync(
long skip,
int take,
CancellationToken ct)
{
var query = _products
.OrderBy("CreatedDate", ignoreCase: false)
.SkipLong(skip)
.Take(take);
return await query.ToArrayAsync(ct);
}
// Check availability
public async Task<bool> HasProductsInStockAsync(CancellationToken ct)
{
return await _products
.Where(p => p.Stock > 0)
.AnyAsync(ct);
}
// Process async stream
public async Task ProcessProductStreamAsync(
IAsyncEnumerable<Product> productStream,
CancellationToken ct)
{
var expensiveProducts = productStream
.Where(p => p.Price > 1000)
.OrderByDescending(p => p.Price)
.Take(10);
var products = await expensiveProducts.ToListAsync(ct);
foreach (var product in products)
{
Console.WriteLine($"{product.Name}: ${product.Price}");
}
}
// Batch processing
public async Task ProcessInBatchesAsync(
IAsyncEnumerable<Product> products,
CancellationToken ct)
{
var batches = products.Chunk(100);
await foreach (var batch in batches.WithCancellation(ct))
{
await ProcessBatchAsync(batch, ct);
}
}
private async Task ProcessBatchAsync(Product[] batch, CancellationToken ct)
{
// Process batch
await Task.Delay(100, ct);
Console.WriteLine($"Processed batch of {batch.Length} products");
}
// Convert and aggregate
public async Task<decimal> CalculateTotalValueAsync(
IEnumerable<Product> products,
CancellationToken ct)
{
var asyncProducts = products.ToAsyncEnumerable();
return await asyncProducts
.Where(p => p.Stock > 0)
.AggregateAsync(0m, (sum, p) => sum + (p.Price * p.Stock), ct);
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public DateTime CreatedDate { get; set; }
}
The library automatically provides IAsyncEnumerable extension methods for frameworks that don't include them natively (pre-.NET 10.0).
await using or WithCancellation()This library is part of the Ecng framework. See the project root for license information.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 net6.0 is compatible. net6.0-android net6.0-android was computed. net6.0-ios net6.0-ios was computed. net6.0-maccatalyst net6.0-maccatalyst was computed. net6.0-macos net6.0-macos was computed. net6.0-tvos net6.0-tvos was computed. net6.0-windows net6.0-windows was computed. net7.0 net7.0 was computed. net7.0-android net7.0-android was computed. net7.0-ios net7.0-ios was computed. net7.0-maccatalyst net7.0-maccatalyst was computed. net7.0-macos net7.0-macos was computed. net7.0-tvos net7.0-tvos was computed. net7.0-windows net7.0-windows was computed. net8.0 net8.0 was computed. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 was computed. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 5 NuGet packages that depend on Ecng.Linq:
| Package | Downloads |
|---|---|
|
StockSharp.Messages
Trading messages (register order, cancel order, subscribe market data etc.). More info on web site https://stocksharp.com/store/ |
|
|
StockSharp.Web.Api.Interfaces
StockSharp WebApi |
|
|
StockSharp.Web.Api.Client
StockSharp WebApi |
|
|
StockSharp.BinanceHistory
Binance History connector. More info on web site https://stocksharp.com/store/ |
|
|
Ecng.Data.ORM
Ecng system framework |
Showing the top 1 popular GitHub repositories that depend on Ecng.Linq:
| Repository | Stars |
|---|---|
|
StockSharp/StockSharp
Algorithmic trading and quantitative trading open source platform to develop trading robots (stock markets, forex, crypto, bitcoins, and options).
|
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.176 | 161 | 6/17/2026 |
| 1.0.175 | 280 | 6/12/2026 |
| 1.0.174 | 819 | 5/15/2026 |
| 1.0.173 | 118 | 5/14/2026 |
| 1.0.172 | 275 | 5/3/2026 |
| 1.0.171 | 619 | 4/14/2026 |
| 1.0.170 | 1,276 | 3/17/2026 |
| 1.0.169 | 145 | 3/17/2026 |
| 1.0.168 | 1,399 | 3/3/2026 |
| 1.0.167 | 308 | 2/28/2026 |
| 1.0.166 | 1,291 | 2/4/2026 |
| 1.0.165 | 728 | 2/1/2026 |
| 1.0.164 | 179 | 1/29/2026 |
| 1.0.163 | 157 | 1/22/2026 |
| 1.0.162 | 227 | 1/19/2026 |
| 1.0.161 | 144 | 1/18/2026 |
| 1.0.160 | 143 | 1/18/2026 |
| 1.0.159 | 207 | 1/14/2026 |
| 1.0.158 | 142 | 1/13/2026 |
| 1.0.157 | 141 | 1/13/2026 |
Linq: align the net6 AsyncEnumerable compat shim with BCL semantics