Skip to content

Commit abaa8f7

Browse files
Develop (#69)
* 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>
1 parent 0da0664 commit abaa8f7

16 files changed

+499
-13
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<MajorVersion>1</MajorVersion>
55
<MinorVersion>1</MinorVersion>
6-
<PatchVersion>4</PatchVersion>
6+
<PatchVersion>5</PatchVersion>
77
<RevisionVersion>0</RevisionVersion>
88
<VersionPrefix>$(MajorVersion).$(MinorVersion).$(PatchVersion)</VersionPrefix>
99
<FileVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion).$(RevisionVersion)</FileVersion>

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ trees to handle asynchronous workflows and other language constructs.
2323

2424
* **Other Expressions**
2525
* `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters.
26+
* `ConfigurationExpression`: An expression that allows access to IConfiguration.
27+
* `InjectExpression`: An expression that allows for depency inject from a IServiceProvider.
2628
* `DebugExpression`: An expression that helps when debugging expression trees.
2729

2830
* Supports Fast Expression Compiler (FEC) for improved performance.

docs/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ trees to handle asynchronous workflows and other language constructs.
2828

2929
* **Other Expressions**
3030
* `StringFormatExpression`: An expression that creates a string using a supplied format string and parameters.
31+
* `ConfigurationExpression`: An expression that allows access to IConfiguration.
32+
* `InjectExpression`: An expression that allows for depency inject from a IServiceProvider.
3133
* `DebugExpression`: An expression that helps when debugging expression trees.
3234

3335
* Supports Fast Expression Compiler (FEC) for improved performance.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using System.Linq.Expressions;
2+
3+
namespace Hyperbee.Expressions;
4+
5+
public static partial class ExpressionExtensions
6+
{
7+
public static Func<TResult> Compile<TResult>(
8+
this Expression<Func<TResult>> expression,
9+
IServiceProvider serviceProvider,
10+
bool preferInterpretation = false )
11+
{
12+
ArgumentNullException.ThrowIfNull( serviceProvider, nameof( serviceProvider ) );
13+
14+
var setter = new ServiceSetter( serviceProvider );
15+
16+
return setter.Visit( expression ) is Expression<Func<TResult>> replacedExpression
17+
? replacedExpression.Compile( preferInterpretation )
18+
: throw new InvalidOperationException( "Failed to compile expression." );
19+
}
20+
21+
public static Delegate Compile(
22+
this LambdaExpression expression,
23+
IServiceProvider serviceProvider,
24+
bool preferInterpretation = false )
25+
{
26+
ArgumentNullException.ThrowIfNull( serviceProvider, nameof( serviceProvider ) );
27+
28+
var setter = new ServiceSetter( serviceProvider );
29+
30+
return setter.Visit( expression ) is LambdaExpression replacedExpression
31+
? replacedExpression.Compile( preferInterpretation )
32+
: throw new InvalidOperationException( "Failed to compile expression." );
33+
}
34+
35+
private class ServiceSetter( IServiceProvider serviceProvider ) : ExpressionVisitor
36+
{
37+
protected override Expression VisitExtension( Expression node )
38+
{
39+
switch ( node )
40+
{
41+
case IDependencyInjectionExpression injectExpression:
42+
injectExpression.SetServiceProvider( serviceProvider );
43+
return node;
44+
default:
45+
return base.VisitExtension( node );
46+
}
47+
}
48+
}
49+
}

src/Hyperbee.Expressions/CompilerServices/EnumerableStateMachineBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ FieldInfo[] fields
103103
typeof( YieldMoveNextDelegate<> ).MakeGenericType( stateMachineType ),
104104
Block(
105105
[success],
106-
TryFinally( // This should be a try fault, but fails in FEC
106+
// This should be a try fault, but fails with preferInterpretation (see: https://github.com/dotnet/runtime/issues/114081)
107+
TryFinally(
107108
Block(
108109
CreateBody(
109110
fields,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Linq.Expressions;
2+
using System.Reflection;
3+
using Microsoft.Extensions.Configuration;
4+
5+
namespace Hyperbee.Expressions;
6+
7+
public class ConfigurationExpression : Expression, IDependencyInjectionExpression
8+
{
9+
private IConfiguration _configuration;
10+
private readonly Type _type;
11+
12+
public ConfigurationExpression( Type type, string key )
13+
{
14+
_type = type;
15+
Key = key;
16+
}
17+
18+
public ConfigurationExpression( Type type, IConfiguration configuration, string key )
19+
{
20+
_type = type;
21+
_configuration = configuration;
22+
Key = key;
23+
}
24+
25+
public void SetServiceProvider( IServiceProvider serviceProvider )
26+
{
27+
_configuration ??= serviceProvider?.GetService( typeof( IConfiguration ) ) as IConfiguration;
28+
}
29+
30+
public override ExpressionType NodeType => ExpressionType.Extension;
31+
public override Type Type => _type;
32+
public string Key { get; init; }
33+
public override bool CanReduce => true;
34+
35+
private MethodInfo GetValueMethodInfo => typeof( ConfigurationBinder )
36+
.GetMethod( "GetValue", [typeof( IConfiguration ), typeof( string )] )!
37+
.MakeGenericMethod( _type );
38+
39+
public override Expression Reduce()
40+
{
41+
if ( _configuration == null )
42+
{
43+
throw new InvalidOperationException( "Configuration is not available." );
44+
}
45+
46+
return Call(
47+
null,
48+
GetValueMethodInfo,
49+
[Constant( _configuration ), Constant( Key )] );
50+
}
51+
52+
protected override Expression VisitChildren( ExpressionVisitor visitor )
53+
{
54+
return this;
55+
}
56+
}
57+
public static partial class ExpressionExtensions
58+
{
59+
public static ConfigurationExpression ConfigurationValue( Type type, string key )
60+
{
61+
return new ConfigurationExpression( type, key );
62+
}
63+
64+
public static ConfigurationExpression ConfigurationValue( Type type, IConfiguration configuration, string key )
65+
{
66+
return new ConfigurationExpression( type, configuration, key );
67+
}
68+
69+
public static ConfigurationExpression ConfigurationValue<T>( string key )
70+
{
71+
return new ConfigurationExpression( typeof( T ), key );
72+
}
73+
74+
public static ConfigurationExpression ConfigurationValue<T>( IConfiguration configuration, string key )
75+
{
76+
return new ConfigurationExpression( typeof( T ), configuration, key );
77+
}
78+
}

src/Hyperbee.Expressions/EnumerableBlockExpression.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Collections.ObjectModel;
1+
using System.Collections.ObjectModel;
42
using System.Linq.Expressions;
53
using Hyperbee.Collections;
64
using Hyperbee.Expressions.CompilerServices;

src/Hyperbee.Expressions/Hyperbee.Expressions.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<None Include="..\..\LICENSE" Pack="true" Visible="false" PackagePath="/" />
4646
<PackageReference Include="Hyperbee.Collections" Version="2.4.0" />
4747
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
48+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.3" />
49+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.3" />
50+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
4851
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="8.0.0">
4952
<PrivateAssets>all</PrivateAssets>
5053
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Hyperbee.Expressions;
2+
3+
public interface IDependencyInjectionExpression
4+
{
5+
void SetServiceProvider( IServiceProvider serviceProvider );
6+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System.Linq.Expressions;
2+
using System.Reflection;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace Hyperbee.Expressions;
6+
7+
public class InjectExpression : Expression, IDependencyInjectionExpression
8+
{
9+
private IServiceProvider _serviceProvider;
10+
private readonly Type _type;
11+
12+
public InjectExpression( Type type, IServiceProvider serviceProvider, string key, Expression defaultValue )
13+
{
14+
_type = type;
15+
_serviceProvider = serviceProvider;
16+
Key = key;
17+
DefaultValue = defaultValue;
18+
}
19+
20+
public void SetServiceProvider( IServiceProvider serviceProvider )
21+
{
22+
_serviceProvider ??= serviceProvider;
23+
}
24+
25+
public override ExpressionType NodeType => ExpressionType.Extension;
26+
public override Type Type => _type;
27+
public string Key { get; init; }
28+
public Expression DefaultValue { get; init; }
29+
public override bool CanReduce => true;
30+
31+
private MethodInfo GetServiceMethodInfo => typeof( ServiceProviderServiceExtensions )
32+
.GetMethod( "GetService", [typeof( IServiceProvider )] )!
33+
.MakeGenericMethod( _type );
34+
35+
private MethodInfo GetKeyedServiceMethodInfo => typeof( ServiceProviderKeyedServiceExtensions )
36+
.GetMethod( "GetKeyedService", [typeof( IServiceProvider ), typeof( string )] )!
37+
.MakeGenericMethod( _type );
38+
39+
public override Expression Reduce()
40+
{
41+
if ( _serviceProvider == null )
42+
{
43+
throw new InvalidOperationException( "Service provider is not available." );
44+
}
45+
46+
var serviceValue = Variable( _type, "serviceValue" );
47+
48+
var callExpression = Key == null
49+
? Call(
50+
null,
51+
GetServiceMethodInfo,
52+
Constant( _serviceProvider )
53+
)
54+
: Call(
55+
null,
56+
GetKeyedServiceMethodInfo,
57+
[Constant( _serviceProvider ), Constant( Key )]
58+
);
59+
60+
var defaultExpression = DefaultValue ??
61+
Throw( New( ExceptionHelper.ConstructorInfo, Constant( "Service is not available." ) ), _type );
62+
63+
return Block(
64+
[serviceValue],
65+
Assign( serviceValue, callExpression ),
66+
Condition(
67+
NotEqual( serviceValue, Constant( null, _type ) ),
68+
serviceValue,
69+
defaultExpression
70+
)
71+
);
72+
}
73+
74+
protected override Expression VisitChildren( ExpressionVisitor visitor )
75+
{
76+
return this;
77+
}
78+
}
79+
80+
internal static class ExceptionHelper
81+
{
82+
internal static readonly ConstructorInfo ConstructorInfo = typeof( InvalidOperationException )
83+
.GetConstructor( [typeof( string )] );
84+
}
85+
86+
public static partial class ExpressionExtensions
87+
{
88+
public static InjectExpression Inject( Type type, IServiceProvider serviceProvider, string key = null, Expression defaultValue = null )
89+
{
90+
return new InjectExpression( type, serviceProvider, key, defaultValue );
91+
}
92+
93+
public static InjectExpression Inject( Type type, string key = null, Expression defaultValue = null )
94+
{
95+
return new InjectExpression( type, null, key, defaultValue );
96+
}
97+
98+
public static InjectExpression Inject<T>( IServiceProvider serviceProvider, string key = null, Expression defaultValue = null )
99+
{
100+
return new InjectExpression( typeof( T ), serviceProvider, key, defaultValue );
101+
}
102+
103+
public static InjectExpression Inject<T>( string key = null, Expression defaultValue = null )
104+
{
105+
return new InjectExpression( typeof( T ), null, key, defaultValue );
106+
}
107+
}

0 commit comments

Comments
 (0)