
Security News
Open Source CAI Framework Handles Pen Testing Tasks up to 3,600× Faster Than Humans
CAI is a new open source AI framework that automates penetration testing tasks like scanning and exploitation up to 3,600× faster than humans.
topmarksdevelopment.expressionbuilder.operations.notbetweenexclusive
Advanced tools
If you're looking for a library to help you easily build lambda expressions, look no further than the Expression Builder package.
With this library you can quickly create a filter that can be applied to a list/enumerable or database query; even dynamically. Plus, it's packed with some great features too!
The Expression Builder offers a wide range of features, including:
IQueryable
/IEnumerable
collections. (See examples here)byte[]
(for compact storage).x => x.Name.Replace(" ", "")
IEnumerable<>
properties and groups (i.e. (x || y) && z
).x => x.Name
) or by string, such as "Name" (when following these conventions)..Add()
method or by operation (like .Equal(...)
)
SmartSearch
method
To install the Expression Builder, you can use the .NET CLI, Package Manager console, or another method of your choice. You can find these installation methods and more information about the package on the NuGet package.
But, for example, to install the package using the .NET CLI, run the following command:
dotnet add package TopMarksDevelopment.ExpressionBuilder
If you find any errors or realise there's a missing feature, feel free to leave comments or open issues.
If you opt to add filters using string
references, you must follow these conventions:
Id
, Name
, Gender
, etc.)Birth.Country
- again, with correctly referenced names[]
after the name. So, a person (with multiple contact points) can have their "Contacts Type" referenced by Contacts[].Type
options
property (like the .Replace(.., ..)
string method)All examples are based on the below set of classes
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public PersonGender Gender { get; set; }
public BirthData Birth { get; set; }
public List<Contact> Contacts { get; private set; }
public Company Employer { get; set; }
}
public enum PersonGender
{
Male,
Female,
Other,
PreferNotToSay
}
public class BirthData
{
public DateTime Date { get; set; }
public string Country { get; set; }
}
public class Contact
{
public ContactType Type { get; set; }
public string Value { get; set; }
public string Comments { get; set; }
}
public enum ContactType
{
Telephone,
Email
}
public class Company {
public string Name { get; set; }
public string Industry { get; set; }
}
Filter<TClass>
We can use the main class of this package directly, the Filter<TClass>
class.
// Chain operation calls to build your query
var filter = new Filter<Person>();
filter.Between("Id", 2, 4)
.And()
.IsNotNull("Birth.Country")
.And()
.EndsWith("Contacts[].Value", "@email.com")
.Or()
.SmartSearch("Name", "\"John\"");
// OR
// Chain Add calls to build your query
var filter = new Filter<Person>();
filter.Add("Id", Operation.Between, 2, 4)
.And()
.Add("Birth.Country", Operation.IsNotNull)
.And()
.Add("Contacts[].Value", Operation.EndsWith, "@email.com")
.Or()
.Add("Name", Operation.SmartSearch, "\"John\"");
// OR
// Add each statement line by line
// (this works for operation calls too)
var filter = new Filter<Person>();
filter.Add("Id", Operation.Between, 2, 4, Connector.And);
filter.Add("Contacts[].Value", Operation.EndsWith, "@email.com", Connector.And);
filter.Add("Birth.Country", Operation.IsNotNull, Connector.Or);
filter.Add("Name", Operation.SmartSearch, "\"John\"");
var people = People.Where(filter);
// Now apply either of these filters
var people = People.Where(filter);
If you find yourself needing the same operation but with different terms. Save yourself from repeating lines, thanks to list matching.
Just pass an array of values and it will query all those terms - on one line!
Let's say we want to find a "Bright Blue Bicycle"! Simple, split the term and filter by it.
var filter = new Filter<Products>();
var termArr = "Bright Blue Bicycle".Split(' ');
var options = new FilterStatementOptions() { Match = Matches.All };
// Connector and Matches are defaulted.
// So, giving an array works in the same way. No extra code required!
// Note: the default for "Contains" is any
filter.Contains(x => x.Name, termArr);
// or
// Declare `options`, so we're matching all!
filter.Contains("Name", termArr, options);
// or
// Declare `options` and `connector` too if you want/need to
filter.Contains("Name", termArr, options, Connector.And);
Complex expressions are handled by grouping filter statements, like in the example below.
Here we are using the fluent API with operation calls on a filter class:
var filter = new Filter<People>();
filter
.OpenGroup()
.OpenGroup()
.DoesNotContain(p => p.Name, "doe")
.Or()
.OpenGroup()
.EndsWith(p => p.Name, "Doe")
.Or()
.StartsWith(p => p.Name, "Jo")
.CloseGroup()
.CloseGroup()
.And()
.IsNull(p => p.Employer)
.CloseGroup()
.Or()
.Equal(p => p.Birth.Country, "GB");
// Now let's apply the filter to our DB Context
var people = myDbContext.People.Where(filter);
This would produce an expression like this: (Excluding all the NotNull
checks and .Trim().ToLower()
functions)
myDbContext.People
.Where(p =>
(
(
!p.Name.Contains("doe")
|| ( p.Name.EndsWith("Doe")
|| p.Name.StartsWith("Jo") )
) &&
p.Employer == null
) &&
p.Birth.Country == "USA"
);
Every time you start a group that means all further statements will be at the same "level/parenthesis" until CloseGroup is called.
You can even add groups to groups! (For those super complex expressions)
IQueryable<TClass>
or IEnumerable<TClass>
You can also filter directly from your enumerable to simplify and improve the readability of your code.
.ToFilterable()
on your IEnumerable
.AsFilterable()
on your IQueryable
After this, you will have access to a bunch of new methods!
This example uses the fluent API and (mostly) property expressions
var filteredPeople =
myDbContext.People
.AsFilterable()
.Equal(x => x.Id, 1)
.Or()
.OpenCollection(x => x.Contacts)
// We're now bound to the Contact type
.EndsWith(x => x.Value, "@email.com")
.Or()
.StartsWith(x => x.Comment, "Test")
// We add this type to correct the reference
.CloseCollection<Person>()
// or use an the expression `x => x.Person` (if that property exists)
.Or()
// We can use the string notion too
.EndsWith("Contacts[].Value", "@email.com");
SmartSearch
This package also includes a handy method called SmartSearch
. This allows you to pass a single term (or a string[]
of terms) to be subject to some "smart" checks:
"Blue"
) will search for this exact term; meaning it's not buried inside a word (so, Steelblue
will not be included)-bright
) will exclude anything containing this term. You can also enclose the term in quotes to exclude an exact word/set of words (i.e. -"bright"
)⚠ A single value will be treated as a single term (regardless of spaces) - it must be parsed. Use
SmartSearch.SplitTerm(string input)
to parse a string (see the example)
This example uses property expressions on a filterable IQueryable
var term = "Term1 Term2 -IgnoreTerm3 \"Exact term\" -\"Ignore exact term\"";
var smartTerms = SmartSearch.SplitTerm(term);
// smartTerms is now an array as shown below
// [ Term1, Term2, -IgnoreTerm3, "Exact term", -"Ignore exact term"];
var filteredPeople =
myDbContext.People
.AsFilterable()
.SmartsSearch(x => x.Name, smartTerms);
You can serialize an expression so it can be reused later.
We support two different serialization methods:
byte[]
/stream (for more compact storage on disk or a DB)If you wanted to store the filter in a human-friendly way, you can use JSON serialisation.
using System.Text.Json;
// Create the filter
var filter = new Filter<Category>();
// Build the filter
filter
.OpenCollection(x => x.Products)
.Equal(x => x.Name, "Product 2")
.Or()
.Equal(x => x.Id, 2)
.Or()
.OpenCollection(x => x.Categories)
.Equal(x => x.Id, 1)
.Or()
.Equal(x => x.Id, 2)
.CloseCollection<Category>()
.CloseCollection<Category>()
.And()
.Equal(x => x.Id, 2);
// To serialise the filter
var jsonString =
JsonSerializer.Serialize(_filter);
// To deserialise the filter
var filterFromJson =
JsonSerializer.Deserialize<Filter<Category>>(jsonString);
Byte[]
/Stream serialisation (Protobuf)When you want to optimise the storage size of your expression, you can serialise it into a byte[]
or stream.
// Using the `filter` that was built above
// Serialize to a Stream (specifically a FileStream)
using (var file = File.Create("./MyPath/File.dat"))
filter.SerializeTo(file);
// Deserialize from a Stream (specifically a FileStream)
using var rFile = File.OpenRead("./MyPath/File.dat");
var filterFromFile = Filter<Category>.DeserializeFrom(rFile);
// Serialize to a byte[]
filter.SerializeTo(out var bytes);
// Deserialize from a byte[]
var filterFromBytes = Filter<Category>.DeserializeFrom(bytes);
This storage method, combined with the .proto
file means you can actually transfer this data through a GRPC message. Just create a service that consumes the FilterGroup
message and you're away.
These are all the operations included in the main package:
FAQs
Unknown package
We found that topmarksdevelopment.expressionbuilder.operations.notbetweenexclusive demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CAI is a new open source AI framework that automates penetration testing tasks like scanning and exploitation up to 3,600× faster than humans.
Security News
Deno 2.4 brings back bundling, improves dependency updates and telemetry, and makes the runtime more practical for real-world JavaScript projects.
Security News
CVEForecast.org uses machine learning to project a record-breaking surge in vulnerability disclosures in 2025.