Skip to content

Commit 387fee0

Browse files
MattEdwardsWaggleBeegithub-actions[bot]github-actions
authored
Release v1.2.0 (#78)
* [FEATURE]: Add Support for IEnumerable Yield (#62) * Add Support for IEnumerable Yield (#62) * Added new YieldStateMachineBuilder to handle IEnumerable * Refactored lowering into subclasses * Refactored StateMachineContext * Initial document and tests for enumerable For, Foreach, While --------- Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> * Renamed some of the class to reflect being Enumerable vs yield * clean up and simplify documentation * Fix issue with preferInterpretation MS has a bug with TryFault and preferInterpretation, so switched to tracking returns and using a Try/Finally * clean up * Clean up test and submit bug to dotnet/runtime * Previous version was 'v1.1.4'. Version now 'v1.1.5'. * [FEATURE]: Add Support for Dependency Injection and Configuration (#67) - New Inject<T>() Expression to help with DI - New ConfigurationValue<T>() Expression to help with getting Config values - New Compile extension to help support DI and Configuration --------- Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> * updates for passing type * Updated code formatting to match rules in .editorconfig * Expose properties for Inject and Config * Add Delegate based service provider Compile * update readme * Updated code formatting to match rules in .editorconfig * [FEATURE]: Create Labs Project for Ideas and Samples (#72) * Fix issue with using and double variable resolver * Introduces a new "lab" project for experimenting with expressions. Adds a FetchExpression for making HTTP requests within expression trees, leveraging IHttpClientFactory for named client support and header/content customization. Includes JsonExpression and JsonPathExpression for deserializing JSON data into specified types or extracting data using JSON Path, integrating with JsonSerializerOptions from the service provider. * Adds expression extensions for data manipulation Introduces `Map` and `Reduce` expressions for transforming collections within expression trees, enabling advanced data manipulation. Add extension methods to `FetchExpression` to simplify reading response content as JSON, text, bytes, or stream, enhancing web request processing capabilities. --------- Co-authored-by: Matt Edwards <matthew.edwards@wagglebee.net> * Previous version was 'v1.1.5'. Version now 'v1.2.0'. * Simplifies expression compilation. Modifies the expression compilation process to directly return the compiled result, removing the unnecessary wrapping of the expression in a `Func<>`. This enhancement supports all delegates and prevents unnecessary casting or wrapping. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: MattEdwardsWaggleBee <MattEdwardsWaggleBee@users.noreply.github.com> Co-authored-by: github-actions <github-actions@github.com>
1 parent 926aa36 commit 387fee0

17 files changed

+1524
-14
lines changed

Directory.Build.props

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
<!-- Solution version numbers -->
33
<PropertyGroup>
44
<MajorVersion>1</MajorVersion>
5-
<MinorVersion>1</MinorVersion>
6-
<PatchVersion>5</PatchVersion>
5+
<MinorVersion>2</MinorVersion>
6+
<PatchVersion>0</PatchVersion>
77
<RevisionVersion>0</RevisionVersion>
88
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
99
<FileVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion).$(RevisionVersion)</FileVersion>

Hyperbee.Expressions.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Expressions.Tests"
3939
EndProject
4040
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hyperbee.Expressions", "src\Hyperbee.Expressions\Hyperbee.Expressions.csproj", "{FE9D5B52-7683-4B78-A7E4-2DB55EE61F22}"
4141
EndProject
42+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hyperbee.Expressions.Lab", "src\Hyperbee.Expressions.Lab\Hyperbee.Expressions.Lab.csproj", "{19728685-37C6-9A6A-128F-7D3D965E2E20}"
43+
EndProject
4244
Global
4345
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4446
Debug|Any CPU = Debug|Any CPU
@@ -57,6 +59,10 @@ Global
5759
{FE9D5B52-7683-4B78-A7E4-2DB55EE61F22}.Debug|Any CPU.Build.0 = Debug|Any CPU
5860
{FE9D5B52-7683-4B78-A7E4-2DB55EE61F22}.Release|Any CPU.ActiveCfg = Release|Any CPU
5961
{FE9D5B52-7683-4B78-A7E4-2DB55EE61F22}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{19728685-37C6-9A6A-128F-7D3D965E2E20}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{19728685-37C6-9A6A-128F-7D3D965E2E20}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{19728685-37C6-9A6A-128F-7D3D965E2E20}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{19728685-37C6-9A6A-128F-7D3D965E2E20}.Release|Any CPU.Build.0 = Release|Any CPU
6066
EndGlobalSection
6167
GlobalSection(SolutionProperties) = preSolution
6268
HideSolutionNode = FALSE
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
using System.Linq.Expressions;
2+
using System.Net.Http.Headers;
3+
using System.Reflection;
4+
using Microsoft.Extensions.DependencyInjection;
5+
6+
using static Hyperbee.Expressions.ExpressionExtensions;
7+
8+
namespace Hyperbee.Expressions.Lab;
9+
10+
public class FetchExpression : Expression, IDependencyInjectionExpression
11+
{
12+
public Expression Url { get; }
13+
public Expression Method { get; }
14+
public Expression Headers { get; }
15+
public Expression Content { get; }
16+
public Expression ClientName { get; }
17+
18+
private IServiceProvider _serviceProvider;
19+
20+
public FetchExpression(
21+
Expression url,
22+
Expression method,
23+
Expression headers = null,
24+
Expression content = null,
25+
Expression clientName = null )
26+
{
27+
ArgumentNullException.ThrowIfNull( url, nameof( url ) );
28+
ArgumentNullException.ThrowIfNull( method, nameof( method ) );
29+
30+
if ( url.Type != typeof( string ) )
31+
throw new ArgumentException( "Url must be of type string.", nameof( url ) );
32+
33+
if ( method.Type != typeof( HttpMethod ) )
34+
throw new ArgumentException( "Method must be of type HttpMethod.", nameof( method ) );
35+
36+
if ( headers != null && headers.Type != typeof( Dictionary<string, string> ) )
37+
throw new ArgumentException( "Headers must be of type Dictionary<string, string>.", nameof( headers ) );
38+
39+
Url = url;
40+
Method = method;
41+
Headers = headers;
42+
Content = content;
43+
ClientName = clientName;
44+
}
45+
46+
public override Type Type => typeof( Task<HttpResponseMessage> );
47+
public override ExpressionType NodeType => ExpressionType.Extension;
48+
public override bool CanReduce => true;
49+
50+
private static readonly ConstructorInfo RequestConstructorInfo = typeof( HttpRequestMessage ).GetConstructor( [typeof( HttpMethod ), typeof( string )] );
51+
52+
public override Expression Reduce()
53+
{
54+
if ( _serviceProvider == null )
55+
throw new InvalidOperationException( "IServiceProvider has not been set." );
56+
57+
// Create service provider constant
58+
var providerConst = Constant( _serviceProvider );
59+
60+
Expression resolveHttpClient;
61+
62+
if ( ClientName != null )
63+
{
64+
// Resolve IHttpClientFactory and call CreateClient(name)
65+
var getFactory = Call(
66+
typeof( ServiceProviderServiceExtensions ),
67+
nameof( ServiceProviderServiceExtensions.GetRequiredService ),
68+
[typeof( IHttpClientFactory )],
69+
providerConst
70+
);
71+
72+
var factoryVar = Variable( typeof( IHttpClientFactory ), "factory" );
73+
74+
var assignFactory = Assign( factoryVar, getFactory );
75+
var createClient = Call( factoryVar, nameof( IHttpClientFactory.CreateClient ), null, ClientName );
76+
77+
resolveHttpClient = Block(
78+
[factoryVar],
79+
assignFactory,
80+
createClient
81+
);
82+
}
83+
else
84+
{
85+
// Resolve HttpClient directly
86+
resolveHttpClient = Call(
87+
typeof( ServiceProviderServiceExtensions ),
88+
nameof( ServiceProviderServiceExtensions.GetRequiredService ),
89+
[typeof( HttpClient )],
90+
providerConst
91+
);
92+
}
93+
94+
var clientVar = Variable( typeof( HttpClient ), "client" );
95+
var assignClient = Assign( clientVar, resolveHttpClient );
96+
97+
// Create HttpRequestMessage
98+
var requestVar = Variable( typeof( HttpRequestMessage ), "request" );
99+
100+
var requestCtor = New(
101+
RequestConstructorInfo,
102+
Method,
103+
Url
104+
);
105+
var assignRequest = Assign( requestVar, requestCtor );
106+
107+
var variables = new List<ParameterExpression> { clientVar, requestVar };
108+
var block = new List<Expression> {
109+
assignClient,
110+
assignRequest
111+
};
112+
113+
// Optional headers
114+
if ( Headers != null )
115+
{
116+
var headersVar = Variable( typeof( Dictionary<string, string> ), "headers" );
117+
variables.Add( headersVar );
118+
block.Add( Assign( headersVar, Headers ) );
119+
120+
var kvp = Parameter( typeof( KeyValuePair<string, string> ), "kvp" );
121+
122+
var addHeader = Call(
123+
Property( requestVar, nameof( HttpRequestMessage.Headers ) ),
124+
nameof( HttpRequestHeaders.Add ),
125+
null,
126+
Property( kvp, nameof( KeyValuePair<string, string>.Key ) ),
127+
Property( kvp, nameof( KeyValuePair<string, string>.Value ) )
128+
);
129+
130+
block.Add( ForEach( headersVar, kvp, addHeader ) );
131+
}
132+
133+
// Optional content
134+
if ( Content != null )
135+
{
136+
block.Add( Assign(
137+
Property( requestVar, nameof( HttpRequestMessage.Content ) ),
138+
Content
139+
) );
140+
}
141+
142+
// SendAsync call
143+
var sendCall = Call( clientVar, nameof( HttpClient.SendAsync ), null, requestVar );
144+
145+
return Block(
146+
variables,
147+
block.Append( sendCall )
148+
);
149+
}
150+
151+
public void SetServiceProvider( IServiceProvider serviceProvider )
152+
{
153+
_serviceProvider = serviceProvider;
154+
}
155+
156+
protected override Expression VisitChildren( ExpressionVisitor visitor )
157+
{
158+
return new FetchExpression(
159+
visitor.Visit( Url ),
160+
visitor.Visit( Method ),
161+
Headers != null ? visitor.Visit( Headers ) : null,
162+
Content != null ? visitor.Visit( Content ) : null,
163+
ClientName
164+
);
165+
}
166+
}
167+
168+
public static partial class ExpressionExtensions
169+
{
170+
public static FetchExpression Fetch(
171+
Expression clientName,
172+
Expression url )
173+
{
174+
return new FetchExpression( url, Expression.Constant( HttpMethod.Get ), null, null, clientName );
175+
}
176+
177+
public static FetchExpression Fetch( Expression clientName,
178+
Expression url,
179+
Expression method,
180+
Expression content = null,
181+
Expression headers = null )
182+
{
183+
return new FetchExpression( url, method, headers, content, clientName );
184+
}
185+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System.Linq.Expressions;
2+
using System.Net.Http.Json;
3+
using static System.Linq.Expressions.Expression;
4+
using static Hyperbee.Expressions.ExpressionExtensions;
5+
6+
namespace Hyperbee.Expressions.Lab;
7+
8+
public static partial class ExpressionExtensions
9+
{
10+
public static Expression ReadJson( FetchExpression fetch, Type type )
11+
{
12+
return ReadJson( Await( fetch ), type );
13+
}
14+
15+
public static Expression ReadJson( Expression response, Type type )
16+
{
17+
ArgumentNullException.ThrowIfNull( response, nameof( response ) );
18+
ArgumentNullException.ThrowIfNull( type, nameof( type ) );
19+
20+
if ( response.Type != typeof( HttpResponseMessage ) )
21+
throw new ArgumentException( "Response must be of type HttpResponseMessage.", nameof( response ) );
22+
23+
var readFromJsonMethodInfo = typeof( HttpContentJsonExtensions )
24+
.GetMethod( nameof( HttpContentJsonExtensions.ReadFromJsonAsync ),
25+
[typeof( HttpContent ), typeof( CancellationToken )] )!
26+
.MakeGenericMethod( type );
27+
28+
var content = Property( response, nameof( HttpResponseMessage.Content ) );
29+
return Call(
30+
null,
31+
readFromJsonMethodInfo,
32+
content,
33+
Default( typeof( CancellationToken ) )
34+
);
35+
}
36+
37+
public static Expression ReadText( FetchExpression fetch )
38+
{
39+
return ReadText( Await( fetch ) );
40+
}
41+
42+
public static Expression ReadText( Expression response )
43+
{
44+
ArgumentNullException.ThrowIfNull( response, nameof( response ) );
45+
46+
return Call(
47+
Property( response, nameof( HttpResponseMessage.Content ) ),
48+
nameof( HttpContent.ReadAsStringAsync ),
49+
Type.EmptyTypes
50+
);
51+
}
52+
53+
public static Expression ReadBytes( FetchExpression fetch )
54+
{
55+
return ReadBytes( Await( fetch ) );
56+
}
57+
58+
public static Expression ReadBytes( Expression response )
59+
{
60+
ArgumentNullException.ThrowIfNull( response, nameof( response ) );
61+
62+
return Call(
63+
Property( response, nameof( HttpResponseMessage.Content ) ),
64+
nameof( HttpContent.ReadAsByteArrayAsync ),
65+
Type.EmptyTypes
66+
);
67+
}
68+
69+
public static Expression ReadStream( FetchExpression fetch )
70+
{
71+
return ReadStream( Await( fetch ) );
72+
}
73+
74+
public static Expression ReadStream( Expression response )
75+
{
76+
ArgumentNullException.ThrowIfNull( response, nameof( response ) );
77+
78+
return Call(
79+
Property( response, nameof( HttpResponseMessage.Content ) ),
80+
nameof( HttpContent.ReadAsStreamAsync ),
81+
Type.EmptyTypes
82+
);
83+
}
84+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>disable</Nullable>
7+
<IsPackable>true</IsPackable>
8+
9+
<Authors>Stillpoint Software, Inc.</Authors>
10+
<PackageId>Hyperbee.Expressions.Lab</PackageId>
11+
<PackageReadmeFile>README.md</PackageReadmeFile>
12+
<PackageTags>expression-tree;expressions;lab;samples</PackageTags>
13+
14+
<PackageIcon>icon.png</PackageIcon>
15+
<PackageProjectUrl>https://stillpoint-software.github.io/hyperbee.expressions/</PackageProjectUrl>
16+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
17+
<Copyright>Stillpoint Software, Inc.</Copyright>
18+
<Title>Hyperbee Expressions (lab)</Title>
19+
<Description>Sample Extentions for .NET Expression Trees.</Description>
20+
<RepositoryUrl>https://github.com/Stillpoint-Software/Hyperbee.Expressions</RepositoryUrl>
21+
<RepositoryType>git</RepositoryType>
22+
<PackageReleaseNotes>https://github.com/Stillpoint-Software/Hyperbee.Expressions/releases/latest</PackageReleaseNotes>
23+
</PropertyGroup>
24+
25+
<PropertyGroup>
26+
<DefineConstants>$(DefineConstants);FAST_COMPILER</DefineConstants>
27+
</PropertyGroup>
28+
29+
<ItemGroup>
30+
<None Update="$(MSBuildProjectName).csproj.DotSettings" Visible="false" />
31+
</ItemGroup>
32+
33+
<ItemGroup>
34+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
35+
<_Parameter1>$(AssemblyName).Tests</_Parameter1>
36+
</AssemblyAttribute>
37+
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
38+
<_Parameter1>$(AssemblyName).Benchmark</_Parameter1>
39+
</AssemblyAttribute>
40+
</ItemGroup>
41+
42+
<ItemGroup>
43+
<None Include="..\..\assets\icon.png" Pack="true" Visible="false" PackagePath="/" />
44+
<None Include="..\..\README.md" Pack="true" Visible="true" PackagePath="/" Link="README.md" />
45+
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="/" />
46+
<PackageReference Include="Hyperbee.Collections" Version="2.4.0" />
47+
<PackageReference Include="Hyperbee.Json" Version="3.0.3" />
48+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
49+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.4" />
50+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.4" />
51+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
52+
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.4" />
53+
<ProjectReference Include="..\Hyperbee.Expressions\Hyperbee.Expressions.csproj" />
54+
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="8.0.0">
55+
<PrivateAssets>all</PrivateAssets>
56+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
57+
</PackageReference>
58+
</ItemGroup>
59+
60+
</Project>

0 commit comments

Comments
 (0)