serilog-settings-configuration icon indicating copy to clipboard operation
serilog-settings-configuration copied to clipboard

Add support for delegates values in StringArgumentValue

Open almostchristian opened this issue 3 years ago • 2 comments

Some sinks contain configuration properties/arguments that are delegate types (Action, Func).

The StringArgumentValue class already supports static field and property accessors for non-delegate types, and can be extended to support delegate values that are accessed from static properties, static fields or static non-generic methods. If the argument value points to a method, overload resolution should be done to match the target delegate with the reflected method.

A disadvantage could be that this would encourage the creation of static methods which some view as bad for testability.

Below is my proposed change:

if (TryParseStaticMemberAccessor(argumentValue, out var accessorTypeName, out var memberName))
{
    var accessorType = Type.GetType(accessorTypeName, throwOnError: true);
    // if target type is a delegate, look for a public static non-generic method in the accessor type
    if (typeof(Delegate).IsAssignableFrom(toType))
    {
        var methodCandidates = accessorType.GetTypeInfo().DeclaredMethods
            .Where(x => x.Name == memberName)
            .Where(x => x.IsPublic)
            .Where(x => !x.IsGenericMethod)
            .Where(x => x.IsStatic)
            .ToList();
    
        // if more than 1 candidates, get target type signature and perform overload resolution on candidate methods
        if (methodCandidates.Count > 1)
        {
            var delegateSig = toType.GetMethod("Invoke");
            var delegateParameters = delegateSig.GetParameters().Select(x => x.ParameterType);
            methodCandidates = methodCandidates
                .Where(x => x.ReturnType == delegateSig.ReturnType)
                .Where(x => x.GetParameters().Select(p => p.ParameterType).SequenceEqual(delegateParameters))
                .ToList();
        }
    
        var method = methodCandidates.SingleOrDefault();
  
        if (method != null)
        {
            return method.CreateDelegate(toType);
        }

almostchristian avatar Apr 16 '21 04:04 almostchristian

I guess this would solve my issue with Elasticsearch sink, in which I need to set a Func<,> delegate to option ModifyConnectionSettings but I couldn't find a way to do this.

alexbrina avatar Feb 21 '22 13:02 alexbrina

Great, do it!

Feofilakt avatar Feb 24 '22 08:02 Feofilakt