https://github.com/JeremyLikness/ExpressionPowerTools
Power tools for working with IQueryable and Expression trees.
https://github.com/JeremyLikness/ExpressionPowerTools
Last synced: 25 days ago
JSON representation
Power tools for working with IQueryable and Expression trees.
- Host: GitHub
- URL: https://github.com/JeremyLikness/ExpressionPowerTools
- Owner: JeremyLikness
- License: mit
- Created: 2020-07-24T22:08:27.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2023-02-21T22:57:07.000Z (about 2 years ago)
- Last Synced: 2023-08-11T03:08:03.715Z (almost 2 years ago)
- Language: C#
- Size: 4.71 MB
- Stars: 77
- Watchers: 6
- Forks: 14
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# ExpressionPowerTools



[Skip ahead to documentation](#documentation)
## What Expression Power Tools does
Expression Power Tools is an experiment I created to better understand and more easily work with expressions. The project started with a vision: the goal to be able to write a query on a client (like Blazor WebAssembly) and execute it remotely but securely on the server, like this:
```csharp
var list = await DbClientContext.Query(context => context.Things)
.Where(t => t.IsActive == ActiveFlag &&
EF.Functions.Like(t.Name, $"%{nameFilter}%"))
.OrderBy(t => EF.Property(t, nameof(Thing.Created)))
.ExecuteRemote()
.ToListAsync();
```You can learn more about the implementation of this in the [Blazor Web Assembly quickstart](./docs/endtoend.md).
The central feature of the [Core library](docs/api/ExpressionPowerTools.Core.a.md) is a set of extensions for working with queries and expressions. For example, the following code uses an extension to create the `ConstantExpression` parameters for the `BinaryExpression` then uses `IExpressionEnumerable` to display the expression tree:
```csharp
var expr = Expression.GreaterThan(
Expression.Constant(40),
Expression.Constant(2));
var tree = expr.AsEnumerable(); // casts to IExpressionEnumerable
var treeDisplay = tree.ToString();
```The example sets `treeDisplay` to:
```text
[LogicalBinaryExpression:GreaterThan] : Boolean => (40 > 2)
[ConstantExpression:Constant] : Int32 => 40
[ConstantExpression:Constant] : Int32 => 2
```[See the Jupyter notebook example](./docs/notebooks/displayexpressiontree.ipynb)
A more involved example uses queries. Here is a query and its tree display:
```csharp
var query = querySource
.Where(e => e.Id.StartsWith("a"))
.Select(e => new { CapturedId = e.Id })
.OrderBy(anonType => anonType.CapturedId)
.Take(5);var tree = query.AsEnumerableExpression();
```Calling `tree.ToString()` will result in:
```text
[MethodCallExpression2:Call] : IQueryable`1 => System.Collections.Generic.List`1[Submission#8+Example].Where(e => e.Id.StartsWith("a")).Select(e => new <>f__AnonymousType0#1`1(CapturedId = e.Id)).OrderBy(anonType => anonType.CapturedId).Take(5)
[MethodCallExpression2:Call] : IOrderedQueryable`1 => System.Collections.Generic.List`1[Submission#8+Example].Where(e => e.Id.StartsWith("a")).Select(e => new <>f__AnonymousType0#1`1(CapturedId = e.Id)).OrderBy(anonType => anonType.CapturedId)
[MethodCallExpression2:Call] : IQueryable`1 => System.Collections.Generic.List`1[Submission#8+Example].Where(e => e.Id.StartsWith("a")).Select(e => new <>f__AnonymousType0#1`1(CapturedId = e.Id))
[MethodCallExpression2:Call] : IQueryable`1 => System.Collections.Generic.List`1[Submission#8+Example].Where(e => e.Id.StartsWith("a"))
[ConstantExpression:Constant] : EnumerableQuery`1 => System.Collections.Generic.List`1[Submission#8+Example]
[UnaryExpression:Quote] : Expression`1 => e => e.Id.StartsWith("a")
[Expression1`1:Lambda] : Func`2 => e => e.Id.StartsWith("a")
[TypedParameterExpression:Parameter] : Example => e
[InstanceMethodCallExpression1:Call] : Boolean => e.Id.StartsWith("a")
[PropertyExpression:MemberAccess] : String => e.Id
[TypedParameterExpression:Parameter] : Example => e
[ConstantExpression:Constant] : String => "a"
[UnaryExpression:Quote] : Expression`1 => e => new <>f__AnonymousType0#1`1(CapturedId = e.Id)
[Expression1`1:Lambda] : Func`2 => e => new <>f__AnonymousType0#1`1(CapturedId = e.Id)
[TypedParameterExpression:Parameter] : Example => e
[NewExpression:New] : <>f__AnonymousType0#1`1 => new <>f__AnonymousType0#1`1(CapturedId = e.Id)
[PropertyExpression:MemberAccess] : String => e.Id
[TypedParameterExpression:Parameter] : Example => e
[UnaryExpression:Quote] : Expression`1 => anonType => anonType.CapturedId
[Expression1`1:Lambda] : Func`2 => anonType => anonType.CapturedId
[TypedParameterExpression:Parameter] : <>f__AnonymousType0#1`1 => anonType
[PropertyExpression:MemberAccess] : String => anonType.CapturedId
[TypedParameterExpression:Parameter] : <>f__AnonymousType0#1`1 => anonType
[ConstantExpression:Constant] : Int32 => 5
```To get the "Take" value (results in `take` with value of `5`):
```csharp
var take = tree.MethodsWithName(nameof(Queryable.Take))
.SelectMany(m => m.AsEnumerable().ConstantsOfType())
.First().Value; // 5
```You can inspect branches of the expression tree:
```csharp
var skip = query.HasFragment(q => q.Skip(5)); // false
var take5 = query.HasFragment(q => q.Take(5)); // true
var take10 = query.HasFragment(q => q.Take(10)); // false
```And even make comparisons:
```csharp
var query1 = querySource
.Where(e => e.Id.StartsWith("a"))
.Select(e => new { CapturedId = e.Id })
.OrderBy(anonType => anonType.CapturedId)
.Take(6);var query2 = querySource
.Where(e => e.Id.StartsWith("b"))
.Select(e => new { CapturedId = e.Id })
.OrderBy(anonType => anonType.CapturedId)
.Take(5);var query3 = querySource
.Where(e => e.Id.StartsWith("a"))
.Select(e => new { CapturedId = e.Id })
.OrderBy(anonType => anonType.CapturedId)
.Take(5);var query1eq = query1.IsEquivalentTo(query); // false: Take(6) != Take(5)
var query2eq = query2.IsEquivalentTo(query); // false: StartsWith("b") != StartsWith("a")
var query3eq = query3.IsEquivalentTo(query); // true
```[See these examples in a Jupyter notebook](./docs/notebooks/displayiqueryable.ipynb)
There are many more features so be sure to check out the documentation!
## Documentation
There are a few options for documentation, including:
- [API Documentation](docs/index.md)
- [Quick Start](docs/quickstart.md)
- [Release Notes](./Releases.md)
- [Test Reports](docs/test/index.md)**Documentation Tip** Because the documentation is stored in this repo, you can use the GitHub "Go to file" feature to quickly find documentation for a type or member. Just click _Go to File_ and type the name of the method or type and look for the markdown file. You can infer the type of documentation from the extension:
- `.a.md` - assemblies
- `.ns.md` - namespaces
- `.cs.md` - types (classes)
- `.i.md` - types (interfaces)
- `.ctor.md` - constructors
- `.m.md` - methods
- `.prop.md` - properties### Libraries
The following libraries are available to use.
### Core
[](https://www.nuget.org/packages/ExpressionPowerTools.Core)
[](docs/test//ExpressionPowerTools.Core.test.md)
[](docs/test//ExpressionPowerTools.Core.coverage.md)```powershell
Install-Package ExpressionPowerTools.Core -Version 0.9.4-alpha
```Power tools for working with `IQueryable` and Expression trees. Enables enumeration of the tree, comparisons ("similar" and "equivalent") and interception to take snapshots and/or mutate expressions.
[API Documentation](docs/api/ExpressionPowerTools.Core.a.md)
### Serialization
[](https://www.nuget.org/packages/ExpressionPowerTools.Serialization)
[](docs/test//ExpressionPowerTools.Serialization.test.md)
[](docs/test//ExpressionPowerTools.Serialization.coverage.md)```powershell
Install-Package ExpressionPowerTools.Serialization -Version 0.9.4-alpha
```Power tools for writing client-side queries that can be safely serialized to run on the server.
[API Documentation](docs/api/ExpressionPowerTools.Serialization.a.md)
### ASP.NET Core Middleware for EF Core
[](https://www.nuget.org/packages/ExpressionPowerTools.Serialization.EFCore.AspNetCore)
[](docs/test//ExpressionPowerTools.Serialization.EFCore.AspNetCore.test.md)
[](docs/test//ExpressionPowerTools.Serialization.EFCore.AspNetCore.coverage.md)```powershell
Install-Package ExpressionPowerTools.Serialization.EFCore.AspNetCore -Version 0.9.4-alpha
```Power tools for deserializing queries initiated by remote clients.
[API Documentation](docs/api/ExpressionPowerTools.Serialization.EFCore.AspNetCore.a.md)
### Http Client
[](https://www.nuget.org/packages/ExpressionPowerTools.Serialization.EFCore.Http)
[](docs/test//ExpressionPowerTools.Serialization.EFCore.Http.test.md)
[](docs/test//ExpressionPowerTools.Serialization.EFCore.Http.coverage.md)```powershell
Install-Package ExpressionPowerTools.Serialization.EFCore.Http -Version 0.9.4-alpha
```Power tools for running remote queries over HTTP, for example a Blazor WebAssembly client.
[API Documentation](docs/api/ExpressionPowerTools.Serialization.EFCore.Http.a.md)
### Documentation Generator
The documentation generator is an internal utility that auto-generates API documentation based on comments and reflection. IT directly generates markdown. Also self-documents.
[API Documentation](docs/api/ExpressionPowerTools.Utilities.DocumentGenerator.a.md)