4
4
using Microsoft . Extensions . Configuration ;
5
5
6
6
using Serilog . Configuration ;
7
+ using Serilog . Debugging ;
7
8
8
9
namespace Serilog . Settings . Configuration ;
9
10
10
- class ObjectArgumentValue : IConfigurationArgumentValue
11
+ class ObjectArgumentValue : ConfigurationArgumentValue
11
12
{
12
13
readonly IConfigurationSection _section ;
13
14
readonly IReadOnlyCollection < Assembly > _configurationAssemblies ;
@@ -20,15 +21,15 @@ public ObjectArgumentValue(IConfigurationSection section, IReadOnlyCollection<As
20
21
_configurationAssemblies = configurationAssemblies ?? throw new ArgumentNullException ( nameof ( configurationAssemblies ) ) ;
21
22
}
22
23
23
- public object ? ConvertTo ( Type toType , ResolutionContext resolutionContext )
24
+ public override object ? ConvertTo ( Type toType , ResolutionContext resolutionContext )
24
25
{
25
26
// return the entire section for internal processing
26
27
if ( toType == typeof ( IConfigurationSection ) ) return _section ;
27
28
28
29
// process a nested configuration to populate an Action<> logger/sink config parameter?
29
30
var typeInfo = toType . GetTypeInfo ( ) ;
30
31
if ( typeInfo . IsGenericType &&
31
- typeInfo . GetGenericTypeDefinition ( ) is Type genericType && genericType == typeof ( Action < > ) )
32
+ typeInfo . GetGenericTypeDefinition ( ) is { } genericType && genericType == typeof ( Action < > ) )
32
33
{
33
34
var configType = typeInfo . GenericTypeArguments [ 0 ] ;
34
35
IConfigurationReader configReader = new ConfigurationReader ( _section , _configurationAssemblies , resolutionContext ) ;
@@ -64,9 +65,9 @@ object CreateArray()
64
65
var arrayElementType = toType . GetElementType ( ) ! ;
65
66
var configurationElements = _section . GetChildren ( ) . ToArray ( ) ;
66
67
var array = Array . CreateInstance ( arrayElementType , configurationElements . Length ) ;
67
- for ( int i = 0 ; i < configurationElements . Length ; ++ i )
68
+ for ( var i = 0 ; i < configurationElements . Length ; ++ i )
68
69
{
69
- var argumentValue = ConfigurationReader . GetArgumentValue ( configurationElements [ i ] , _configurationAssemblies ) ;
70
+ var argumentValue = FromSection ( configurationElements [ i ] , _configurationAssemblies ) ;
70
71
var value = argumentValue . ConvertTo ( arrayElementType , resolutionContext ) ;
71
72
array . SetValue ( value , i ) ;
72
73
}
@@ -84,29 +85,28 @@ bool TryCreateContainer([NotNullWhen(true)] out object? result)
84
85
85
86
foreach ( var section in _section . GetChildren ( ) )
86
87
{
87
- var argumentValue = ConfigurationReader . GetArgumentValue ( section , _configurationAssemblies ) ;
88
+ var argumentValue = FromSection ( section , _configurationAssemblies ) ;
88
89
var key = new StringArgumentValue ( section . Key ) . ConvertTo ( keyType , resolutionContext ) ;
89
90
var value = argumentValue . ConvertTo ( valueType , resolutionContext ) ;
90
- addMethod . Invoke ( result , new [ ] { key , value } ) ;
91
+ addMethod . Invoke ( result , [ key , value ] ) ;
91
92
}
92
93
return true ;
93
94
}
94
- else if ( IsConstructableContainer ( toType , elementType , out concreteType , out addMethod ) )
95
+
96
+ if ( IsConstructableContainer ( toType , elementType , out concreteType , out addMethod ) )
95
97
{
96
98
result = Activator . CreateInstance ( concreteType ) ?? throw new InvalidOperationException ( $ "Activator.CreateInstance returned null for { concreteType } ") ;
97
99
98
100
foreach ( var section in _section . GetChildren ( ) )
99
101
{
100
- var argumentValue = ConfigurationReader . GetArgumentValue ( section , _configurationAssemblies ) ;
102
+ var argumentValue = FromSection ( section , _configurationAssemblies ) ;
101
103
var value = argumentValue . ConvertTo ( elementType , resolutionContext ) ;
102
104
addMethod . Invoke ( result , new [ ] { value } ) ;
103
105
}
104
106
return true ;
105
107
}
106
- else
107
- {
108
- return false ;
109
- }
108
+
109
+ return false ;
110
110
}
111
111
}
112
112
@@ -153,58 +153,84 @@ bool TryCallCtorImplicit(
153
153
154
154
bool TryCallCtor ( Type type , Dictionary < string , IConfigurationSection > suppliedArguments , ResolutionContext resolutionContext , [ NotNullWhen ( true ) ] out object ? value )
155
155
{
156
- value = null ;
156
+ var binding = type . GetConstructors ( )
157
+ . Select ( ci =>
158
+ {
159
+ var args = new List < object ? > ( ) ;
160
+ var matches = 0 ;
161
+ var stringMatches = 0 ;
162
+ var suppliedNames = new HashSet < string > ( StringComparer . OrdinalIgnoreCase ) ;
163
+ foreach ( var p in ci . GetParameters ( ) )
164
+ {
165
+ if ( suppliedArguments . TryGetValue ( p . Name ?? "" , out var argValue ) )
166
+ {
167
+ args . Add ( argValue ) ;
168
+ matches += 1 ;
169
+
170
+ if ( p . ParameterType == typeof ( string ) )
171
+ {
172
+ stringMatches += 1 ;
173
+ }
174
+
175
+ if ( p . Name != null )
176
+ {
177
+ suppliedNames . Add ( p . Name ) ;
178
+ }
179
+ }
180
+ else
181
+ {
182
+ if ( p . HasDefaultValue )
183
+ {
184
+ args . Add ( p . DefaultValue ) ;
185
+ }
186
+ else
187
+ {
188
+ return new { ci , args , isCallable = false , matches , stringMatches , suppliedNames } ;
189
+ }
190
+ }
191
+ }
157
192
158
- if ( suppliedArguments . Count == 0 &&
159
- type . GetConstructor ( Type . EmptyTypes ) is ConstructorInfo parameterlessCtor )
193
+ return new { ci , args , isCallable = true , matches , stringMatches , suppliedNames } ;
194
+ } )
195
+ . Where ( binding => binding . isCallable )
196
+ . OrderByDescending ( binding => binding . matches )
197
+ . ThenByDescending ( binding => binding . stringMatches )
198
+ . ThenBy ( binding => binding . args . Count )
199
+ . FirstOrDefault ( ) ;
200
+
201
+ if ( binding == null )
160
202
{
161
- value = parameterlessCtor . Invoke ( [ ] ) ;
162
- return true ;
203
+ value = null ;
204
+ return false ;
163
205
}
164
206
165
- var ctor =
166
- ( from c in type . GetConstructors ( )
167
- from p in c . GetParameters ( )
168
- let argumentBindResult = suppliedArguments . TryGetValue ( p . Name ?? "" , out var argValue ) switch
169
- {
170
- true => new { success = true , hasMatch = true , value = ( object ? ) argValue } ,
171
- false => p . HasDefaultValue switch
172
- {
173
- true => new { success = true , hasMatch = false , value = ( object ? ) p . DefaultValue } ,
174
- false => new { success = false , hasMatch = false , value = ( object ? ) null } ,
175
- } ,
176
- }
177
- group new { argumentBindResult , p . ParameterType } by c into gr
178
- where gr . All ( z => z . argumentBindResult . success )
179
- let matchedArgs = gr . Where ( z => z . argumentBindResult . hasMatch ) . ToList ( )
180
- orderby matchedArgs . Count descending,
181
- matchedArgs . Count ( p => p . ParameterType == typeof ( string ) ) descending
182
- select new
183
- {
184
- ConstructorInfo = gr . Key ,
185
- ArgumentValues = gr . Select ( z => new { Value = z . argumentBindResult . value , Type = z . ParameterType } )
186
- . ToList ( )
187
- } ) . FirstOrDefault ( ) ;
188
-
189
- if ( ctor is null )
207
+ for ( var i = 0 ; i < binding . ci . GetParameters ( ) . Length ; ++ i )
190
208
{
191
- return false ;
209
+ if ( binding . args [ i ] is IConfigurationSection section )
210
+ {
211
+ var argumentValue = FromSection ( section , _configurationAssemblies ) ;
212
+ binding . args [ i ] = argumentValue . ConvertTo ( binding . ci . GetParameters ( ) [ i ] . ParameterType , resolutionContext ) ;
213
+ }
192
214
}
193
215
194
- var ctorArguments = new object ? [ ctor . ArgumentValues . Count ] ;
195
- for ( var i = 0 ; i < ctor . ArgumentValues . Count ; i ++ )
216
+ value = binding . ci . Invoke ( binding . args . ToArray ( ) ) ;
217
+
218
+ foreach ( var pi in type . GetProperties ( BindingFlags . Instance | BindingFlags . Public ) )
196
219
{
197
- var argument = ctor . ArgumentValues [ i ] ;
198
- var valueValue = argument . Value ;
199
- if ( valueValue is IConfigurationSection s )
220
+ if ( ! binding . suppliedNames . Contains ( pi . Name ) && suppliedArguments . TryGetValue ( pi . Name , out var section ) && pi . CanWrite )
200
221
{
201
- var argumentValue = ConfigurationReader . GetArgumentValue ( s , _configurationAssemblies ) ;
202
- valueValue = argumentValue . ConvertTo ( argument . Type , resolutionContext ) ;
222
+ var propertyValue = FromSection ( section , _configurationAssemblies ) ;
223
+ try
224
+ {
225
+ pi . SetValue ( value , propertyValue . ConvertTo ( pi . PropertyType , resolutionContext ) ) ;
226
+ }
227
+ catch ( Exception ex )
228
+ {
229
+ SelfLog . WriteLine ( $ "Serilog.Settings.Configuration: Property setter on { type } failed: { ex } ") ;
230
+ }
203
231
}
204
- ctorArguments [ i ] = valueValue ;
205
232
}
206
233
207
- value = ctor . ConstructorInfo . Invoke ( ctorArguments ) ;
208
234
return true ;
209
235
}
210
236
@@ -262,7 +288,7 @@ static bool IsConstructableDictionary(Type type, Type elementType, [NotNullWhen(
262
288
}
263
289
foreach ( var method in concreteType . GetMethods ( ) )
264
290
{
265
- if ( ! method . IsStatic && method . Name == "Add" )
291
+ if ( method is { IsStatic : false , Name : "Add" } )
266
292
{
267
293
var parameters = method . GetParameters ( ) ;
268
294
if ( parameters . Length == 2 && parameters [ 0 ] . ParameterType == keyType && parameters [ 1 ] . ParameterType == valueType )
@@ -301,7 +327,7 @@ static bool IsConstructableContainer(Type type, Type elementType, [NotNullWhen(t
301
327
}
302
328
foreach ( var method in concreteType . GetMethods ( ) )
303
329
{
304
- if ( ! method . IsStatic && method . Name == "Add" )
330
+ if ( method is { IsStatic : false , Name : "Add" } )
305
331
{
306
332
var parameters = method . GetParameters ( ) ;
307
333
if ( parameters . Length == 1 && parameters [ 0 ] . ParameterType == elementType )
0 commit comments