dadhi / DryIoc

DryIoc is fast, small, full-featured IoC Container for .NET
MIT License
1.01k stars 123 forks source link

Extension methods not being handled correctly in Made.Of service returning expression #576

Closed Metadorius closed 1 year ago

Metadorius commented 1 year ago

Background: I am integrating Serilog in my application. I used your examples with context-based resolution but adapted them to a generic Microsoft.Extensions.Logging + Serilog approach to decouple from Serilog in the business logic code. I got something like this:

var container = new Container()
    .WithMefAttributedModel();

var serilogLogger = new LoggerConfiguration()
    /* ... */
    .CreateLogger();

var loggerFactory = new SerilogLoggerFactory(serilogLogger);

container.RegisterInstance<ILoggerFactory>(loggerFactory);

container.Register(
    Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(),
        f => f.CreateLogger(/*categoryName:*/ null!)),
    setup: Setup.With(condition: r => r.Parent.ImplementationType == null));

container.Register(
    Made.Of(_ => ServiceInfo.Of<ILoggerFactory>(),
        f => f.CreateLogger(Arg.Index<Type>(0)),
        r => r.Parent.ImplementationType),
    setup: Setup.With(condition: r => r.Parent.ImplementationType != null));

It works fine on the first registration because string-accepting method is actually a class method and fails on the second one, which is an extension method, because for some reason extension methods in C# behave differently internally compared to regular instance methods and this is not handled by DryIoc Made.Of handling code.

Non-extension method: image

Extension method: image

As a result it fails because it gets an unexpected non-constant parameter (being the factory which is the extension method is for)

DryIoc.ContainerException: code: Error.UnexpectedExpressionInsteadOfConstantInMadeOf;
message: Expected `ConstantExpression` for value of parameter, property, or field, but found `f` 
in Made.Of expression: `f => f.CreateLogger(Index(0))`
   at DryIoc.Throw.For[T](Int32 error, Object arg0, Object arg1, Object arg2, Object arg3) in /_/src/DryIoc/Container.cs:line 14549
   at DryIoc.Made.GetArgExpressionValueOrThrow(Expression wholeServiceExpr, Expression argExpr) in /_/src/DryIoc/Container.cs:line 7450
   at DryIoc.Made.ComposeParameterSelectorFromArgs(Boolean& hasCustomValue, Expression wholeServiceExpr, ParameterInfo[] paramInfos, IList`1 argExprs, Func`2[] argValues) in /_/src/DryIoc/Container.cs:line 7305
   at DryIoc.Made.FromExpression[TService](Func`2 getFactoryMethodSelector, LambdaExpression serviceReturningExpr, Func`2[] argValues) in /_/src/DryIoc/Container.cs:line 7220
   at DryIoc.Made.Of[TFactory,TService](Func`2 getFactoryInfo, Expression`1 serviceReturningExpr, Func`2[] argValues) in /_/src/DryIoc/Container.cs:line 7149
...
Metadorius commented 1 year ago

How "native" to class method call expression looks: image

Extension methods: image

dadhi commented 1 year ago

@Metadorius Thanks for the finding, will fix Btw, I am accepting the PRs if you're willing to.

Metadorius commented 1 year ago

I would love to help, but I am afraid my expertise on this is not enough 😅

577 I might have an idea on possible ways of solving, though I am not sure about the correct way to solve it there either, should write my thoughts there. Regardless, I might help but not sure how much would be possible for me, so you shouldn't rely on me much.

dadhi commented 1 year ago

That's fine. You are already helping.

dadhi commented 1 year ago

fixed now.